# How Global Keyboard Bindings Work in OpenLogi: Modifier Keys, TOML Syntax, and Runtime Lookup

> Learn how OpenLogi defines global keyboard bindings using TOML syntax, including modifier keys like shift and command. Discover efficient runtime lookup with this comprehensive guide.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: internals
- Published: 2026-08-31

---

**OpenLogi defines global keyboard bindings in a top-level `[keyboard.bindings]` TOML table using trigger strings like `"shift+command+f5"`, parses them into `KeyTrigger` structs with modifier bit masks, and stores them in a thread-safe `BTreeMap` for real-time lookup by the agent's input hook.**

OpenLogi is a Rust-based automation tool that enables system-wide keyboard shortcuts for controlling external devices. Understanding how it handles **global keyboard bindings including modifier keys** is essential for customizing your workflow. The implementation spans configuration parsing, shared state management, and low-level OS hook integration.

## Configuring Global Keyboard Bindings in TOML

OpenLogi uses a plain TOML configuration file that both the GUI and background agent read. Global shortcuts live under the **`[keyboard.bindings]`** table at the root of your config file.

### Basic Syntax and Modifier Support

Each entry maps a trigger string to an action. The trigger combines a key name with optional modifiers joined by `+`:

```toml
[keyboard.bindings]
f1 = "MissionControl"
"shift+command+f5" = "ShowDesktop"

```

From [`docs/config.example.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/config.example.toml) (lines 113-115), you can see both a simple key binding and a compound modifier trigger in practice.

### Supported Modifiers and Aliases

According to [`docs/CONFIGURATION.md`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/CONFIGURATION.md) (lines 84-86), OpenLogi accepts these modifiers:

- **Primary names**: `shift`, `control`, `option`, `command`
- **Common aliases**: `ctrl` (for control), `alt` (for option), `cmd` (for command)

The parser normalizes aliases to canonical names internally, so `"ctrl+alt+delete"` and `"control+option+delete"` are equivalent.

## Parsing Trigger Strings into KeyTrigger Structs

The heart of OpenLogi's keyboard binding system is the `KeyTrigger` struct, implemented in [`crates/openlogi-core/src/config/key_trigger.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config/key_trigger.rs).

### KeyTrigger Structure

When you define `"shift+command+f5"` in TOML, OpenLogi parses it into:

```rust
// Conceptual representation from key_trigger.rs
pub struct KeyTrigger {
    pub key: KeyCode,      // The physical key (e.g., F5)
    pub modifiers: ModifierMask,  // Bit mask: SHIFT | COMMAND
}

```

The `ModifierMask` bit mask enables fast comparisons using simple bitwise operations rather than string matching.

### Parsing Example

```rust
use openlogi_core::config::KeyTrigger;

// Parse from config string
let trigger: KeyTrigger = "ctrl+alt+delete".parse().unwrap();

// The parser:
// 1. Splits on '+' → ["ctrl", "alt", "delete"]
// 2. Normalizes aliases → ["control", "option", "delete"]
// 3. Builds modifier bit mask
// 4. Validates key name against known key codes

```

## Storing and Sharing Bindings at Runtime

Parsed bindings are stored in `Config.keyboard.bindings` as a `BTreeMap<KeyTrigger, Action>`. The agent promotes this to shared state for cross-thread access.

### Shared State Architecture

```rust
// From the agent runtime (conceptual, based on hook.rs)
pub type SharedKeyboardBindings = Arc<RwLock<BTreeMap<KeyTrigger, Action>>>;

// On config reload:
let shared = Arc::new(RwLock::new(config.keyboard.bindings.clone()));

```

This `Arc<RwLock<...>>` pattern allows:
- **Read-heavy access** from the hot path (every key event)
- **Safe updates** when the config file changes
- **Minimal contention** through read-preferring locks

### Runtime Lookup Flow

The global hook in [`crates/openlogi-agent-core/src/runtime/hook.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent-core/src/runtime/hook.rs) handles every system key event:

```rust
fn handle_key(
    event: KeyEvent,
    bindings: &SharedKeyboardBindings,
    dispatcher: &ActionDispatcher
) {
    // Convert OS event to our KeyTrigger format
    let trigger = KeyTrigger::from(event);
    
    // Fast BTreeMap lookup
    if let Some(action) = bindings.read().unwrap().get(&trigger) {
        dispatcher.dispatch(action.clone());
    }
}

```

## Hot-Reloading and Configuration Changes

OpenLogi's agent watches the TOML file for changes, enabling live updates without restart:

1. **File watcher detects change** → triggers config reload
2. **Parse new TOML** → build fresh `BTreeMap<KeyTrigger, Action>`
3. **Acquire write lock** → swap shared bindings atomically
4. **New key events** → immediately use updated mappings

This design ensures zero-downtime configuration changes for global shortcuts.

## Limitations of Global Bindings

Global keyboard bindings have **one key restriction**: they are strictly single-action mappings. Unlike device-specific button bindings, they cannot specify short-press/long-press behavior pairs. This limitation is enforced by the `Action` type's definition—global bindings resolve directly to one action with no duration logic.

## Complete Configuration Example

```toml

# Global shortcuts active regardless of selected device

[keyboard.bindings]
f2 = "PlayPause"
"shift+command+f5" = "ShowDesktop"
"ctrl+alt+delete" = "EmergencyLock"
"cmd+shift+1" = "SwitchProfileWork"
"cmd+shift+2" = "SwitchProfileGaming"

```

## Summary

- Global keyboard bindings are defined in `[keyboard.bindings]` using **trigger strings** with `+`-separated modifiers and keys
- OpenLogi supports **shift, control/ctrl, option/alt, and command/cmd** modifiers with alias normalization
- The `KeyTrigger` struct in [`key_trigger.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/key_trigger.rs) uses **bit-masked modifiers** for efficient storage and comparison
- Parsed bindings live in a **`BTreeMap<KeyTrigger, Action>`** wrapped in `Arc<RwLock<_>>` for thread-safe access
- The agent's **input hook** performs fast lookups on every key event via `handle_key` in [`hook.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/hook.rs)
- Configuration changes are **hot-reloaded** without restarting the agent

## Frequently Asked Questions

### What modifier key aliases does OpenLogi accept?

OpenLogi accepts both canonical names and common aliases. Use `control` or `ctrl`, `option` or `alt`, `command` or `cmd`. The parser internally normalizes all aliases to the canonical form before building the modifier bit mask in `KeyTrigger`.

### Can I use the same key binding for multiple actions?

No. The `BTreeMap<KeyTrigger, Action>` structure enforces unique keys. If you define the same trigger twice in TOML, the second definition overwrites the first. Each global shortcut maps to exactly one action.

### How do global keyboard bindings differ from device button bindings?

Global bindings are system-wide shortcuts defined in `[keyboard.bindings]` that work regardless of which device is selected. Device button bindings are per-device and support advanced features like short-press/long-press detection. Global bindings are strictly single-action.

### Where can I find a complete list of valid key names?

The valid key names are defined in the key parsing implementation within [`crates/openlogi-core/src/config/key_trigger.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config/key_trigger.rs). Common keys include function keys (`f1`-`f24`), alphanumeric keys, `space`, `tab`, `enter`, `escape`, and arrow keys. Refer to [`docs/CONFIGURATION.md`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/CONFIGURATION.md) for documented examples and [`docs/config.example.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/config.example.toml) for concrete usage patterns.