# How OpenLogi Identifies Applications for Per-App Profiles: A Deep Dive into Platform-Specific Detection

> Learn how OpenLogi identifies applications for per-app profiles using ForegroundApp types and platform-specific identifiers. Discover bundle IDs, WM_CLASS, app_id, and executable paths.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: deep-dive
- Published: 2026-09-01

---

**OpenLogi identifies applications for per-app profiles using the `ForegroundApp` type, which stores a platform-specific identifier (`id`) as the lookup key and a human-readable `display_name` for the UI, with observers in the `openlogi-hook` crate extracting bundle IDs on macOS, `WM_CLASS` on X11, `app_id` on Wayland, and executable paths on Windows.**

OpenLogi's per-app profile system relies on precise identification of the currently active foreground application. The system is built around a unified abstraction—the `ForegroundApp` struct—that normalizes platform differences while maintaining a strict contract between running applications and user-defined configuration overrides.

## The ForegroundApp Abstraction

At the heart of OpenLogi's detection logic is the `ForegroundApp` struct defined in [`crates/openlogi-core/src/app.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/app.rs). This type serves as the canonical representation of the active application across all supported platforms:

| Field | Purpose | Platform Mapping |
|-------|---------|------------------|
| **`id`** | Exact lookup key for per-app profile matching | Platform-specific identifier |
| **`display_name`** | Human-readable label shown in the UI | Falls back to `id` when unavailable |

The `id` field is the **sole identifier** used for configuration lookups. The UI never exposes this raw value to users; instead, it renders the `display_name` for readability.

## Platform-Specific Extraction Mechanisms

OpenLogi implements dedicated foreground-app observers in the `openlogi-hook` crate, with each platform using native APIs to extract application identity:

### macOS: Bundle Identifier Extraction

In [`crates/openlogi-hook/src/macos.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/macos.rs), OpenLogi leverages the Launch Services framework to obtain the **bundle identifier** (e.g., `com.apple.Safari`) and localized application name when available.

### X11 (Linux): WM_CLASS Parsing

The X11 implementation in [`crates/openlogi-hook/src/linux/wlr_foreign_toplevel.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/linux/wlr_foreign_toplevel.rs) extracts the **`WM_CLASS` class string** from the X11 window properties. This string typically matches the application's `.desktop` file name.

### Wayland (Linux): app_id Retrieval

For Wayland compositors, [`crates/openlogi-hook/src/linux/gnome_shell.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/linux/gnome_shell.rs) reads the **`xdg_app_id`** (or simply `app_id`) provided by the compositor. This follows the `app_id` convention established by `xdg-shell`.

### Windows: Executable Path Normalization

On Windows, [`crates/openlogi-hook/src/windows.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/windows.rs) uses the **lower-cased full executable path** as the identifier. This provides a stable, file-system-backed key that remains consistent across application versions.

## Unified API and IPC Propagation

Each platform observer returns an `Option<ForegroundApp>`, handling detection failures gracefully. These implementations are consolidated through the public API in [`crates/openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/lib.rs):

```rust
/// Returns the current foreground application, if one can be determined.
pub fn frontmost_application() -> Option<ForegroundApp> {
    // internally dispatches to the appropriate platform module
}

```

The `ForegroundApp` value traverses the IPC channel defined in [`crates/openlogi-ipc/src/ipc.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/ipc.rs), connecting the GUI and agent processes. Upon foreground changes, the agent recomputes effective bindings via `Config::effective_bindings` in [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs), applying any per-app overrides from the user's TOML configuration.

## Working with ForegroundApp in Code

The following examples demonstrate practical usage of the detection API:

```rust
use openlogi_hook::frontmost_application;
use openlogi_core::app::ForegroundApp;

/// Get the current foreground app and print its identifier.
fn print_foreground_app() {
    if let Some(app) = frontmost_application() {
        println!("App ID: {}", app.id);
        println!("App Name: {}", app.display_name);
    } else {
        println!("No foreground application detected");
    }
}

```

For cases where platform detection fails or no friendly name exists, the `unnamed` constructor provides a fallback:

```rust
/// Create a dummy app entry when the platform cannot provide a name.
fn make_unnamed_app(id: &str) -> ForegroundApp {
    ForegroundApp::unnamed(id.to_owned())
}

```

## Key Source Files

| Path | Responsibility |
|------|---------------|
| [`crates/openlogi-core/src/app.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/app.rs) | `ForegroundApp` definition and identifier semantics |
| [`crates/openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/lib.rs) | Unified `frontmost_application()` API |
| [`crates/openlogi-hook/src/macos.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/macos.rs) | macOS Launch Services integration |
| [`crates/openlogi-hook/src/linux/wlr_foreign_toplevel.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/linux/wlr_foreign_toplevel.rs) | X11 `WM_CLASS` extraction |
| [`crates/openlogi-hook/src/linux/gnome_shell.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/linux/gnome_shell.rs) | Wayland `app_id` retrieval |
| [`crates/openlogi-hook/src/windows.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/windows.rs) | Windows executable path handling |
| [`crates/openlogi-ipc/src/ipc.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/ipc.rs) | IPC structures for cross-process communication |
| [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs) | Per-app profile resolution logic |

## Summary

- **OpenLogi identifies applications through the `ForegroundApp` struct**, which encapsulates both a stable lookup key (`id`) and a human-readable label (`display_name`)
- **Platform-specific observers extract native identifiers**: bundle IDs on macOS, `WM_CLASS` on X11, `app_id` on Wayland, and executable paths on Windows
- **The `frontmost_application()` function** in `openlogi-hook` provides a unified, cross-platform API
- **IPC propagation** carries detection results from the GUI to the agent core for configuration resolution
- **Per-app profile matching** occurs exclusively against the `ForegroundApp.id` field, ensuring consistent behavior across platforms

## Frequently Asked Questions

### How do I find the correct app ID for my per-app profile configuration?

Query the currently running application using `frontmost_application()` or check OpenLogi's logs. On macOS, use `mdls -name kMDItemCFBundleIdentifier -r /Applications/AppName.app` to verify bundle identifiers. On X11, run `xprop WM_CLASS` and click the target window. On Wayland, `swaymsg -t get_tree` or equivalent compositor commands expose `app_id` values.

### Why does Windows use executable paths instead of more stable identifiers?

Windows lacks a universal, user-friendly application identifier comparable to macOS bundle IDs. The lower-cased executable path provides deterministic, version-agnostic identification without requiring registry parsing or heuristic name matching. This trades occasional path sensitivity for reliability and implementation simplicity.

### Can `ForegroundApp.id` change for the same application across versions?

Platform-dependent. macOS bundle IDs and X11 `WM_CLASS` values rarely change. Windows executable paths remain stable unless the installation directory changes. Wayland `app_id` values are controlled by the application and compositor, making them generally consistent but not guaranteed.

### What happens when foreground detection fails?

The `frontmost_application()` function returns `None`, and the agent falls back to global default bindings. Detection failures may occur during rapid application switching, fullscreen direct-rendering contexts, or when platform APIs are temporarily unavailable. OpenLogi's architecture treats `None` as a valid state, ensuring continuous operability.