# Difference Between openlogi-hook and openlogi-inject Crates in OpenLogi

> Discover the difference between openlogi-hook and openlogi-inject crates. OpenLogi uses them for bidirectional mouse and keyboard control with clean separation.

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

---

**openlogi-hook captures raw OS input events while openlogi-inject synthesizes them, together providing OpenLogi with bidirectional control over mouse and keyboard streams while maintaining clean architectural separation.**

The OpenLogi project (available at `AprilNEA/OpenLogi`) implements advanced input remapping through a modular Rust workspace. Understanding the distinction between the `openlogi-hook` and `openlogi-inject` crates is essential for developers extending the platform or debugging input handling.

## Core Architectural Responsibilities

These crates serve opposite but complementary functions within the OpenLogi architecture.

### Input Capture with openlogi-hook

The **`openlogi-hook`** crate handles **input capture**—listening to raw operating system events before they reach applications. It implements low-level event taps on each platform: `CGEventTap` on macOS, `evdev` grab with `uinput` re-injection on Linux, and `WH_MOUSE_LL` on Windows.

According to the crate overview in [`openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-hook/src/lib.rs), this crate is part of the **agent** process (`openlogi-agent`) and serves as the source of raw events flowing to the GUI, hook logic, and DPI-cycle calculations. The hook owns a *watchdog* that safely disables the tap when Accessibility permissions are revoked, as detailed in [`openlogi-hook/AGENTS.md`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-hook/AGENTS.md).

### Event Synthesis with openlogi-inject

The **`openlogi-inject`** crate handles **input synthesis**—translating logical **Action** objects from the core configuration into native OS events. It dispatches to platform-specific implementations: `CGEvent`/`NSEvent` on macOS, `uinput`/D-Bus on Linux, and `SendInput` on Windows.

As implemented in [`openlogi-inject/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-inject/src/lib.rs), this crate provides a platform-agnostic library used by the core to execute actions. It is imported by both the agent (to send events) and the core (for unit-testable effect simulation), keeping the core schema platform-neutral.

## Platform-Specific Implementation Details

### How openlogi-hook Taps into the OS

The capture layer isolates OS-specific I/O from the core and UI. Key platform implementations include:

- **[`openlogi-hook/src/macos.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-hook/src/macos.rs)** – Contains the `CGEventTap` logic for macOS event interception
- **[`openlogi-hook/src/linux.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-hook/src/linux.rs)** – Implements Linux `evdev` grab and `uinput` handling
- **[`openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-hook/src/lib.rs)** – Exposes the public `Hook` API and `EventDisposition` type

The primary types include `Hook`, `HookEvent`, `MouseEvent`, `KeyEvent`, `EventDevice`, and `EventDisposition`, which allows the callback to decide whether to pass events through or suppress them.

### How openlogi-inject Dispatches Actions

The injection layer translates high-level `Action` objects into hardware events without requiring real hardware for testing. The architecture includes:

- **[`openlogi-inject/src/inject.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-inject/src/inject.rs)** – Contains the core `execute` function that dispatches to platform modules
- **[`openlogi-inject/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-inject/src/lib.rs)** – Provides crate description and public re-exports
- Platform modules ([`inject/macos.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/inject/macos.rs), [`inject/linux.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/inject/linux.rs), [`inject/windows.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/inject/windows.rs)) – Handle actual OS-specific event synthesis

Key types include `execute`, `HeldChord`, `HeldKey`, and `HeldOutput`, which manage the state of injected key combinations.

## Crate Dependencies and Relationships

The dependency relationship is asymmetric:

- **`openlogi-hook` depends on `openlogi-inject`** – When the hook captures events that trigger synthetic actions (such as a gesture generating a click), it uses the inject crate. This dependency is declared in the hook's [`Cargo.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/Cargo.toml).
- **`openlogi-inject` is independent** – It only requires core types (`Action`, `KeyCombo`, etc.) and contains no capture code.

This separation permits unit testing of action-to-event translation without a physical input device, as noted in the comments at the top of [`openlogi-inject/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-inject/src/lib.rs).

## Practical Usage Examples

### Capturing Raw Input Events

To observe mouse movements and keyboard presses without modifying them, use the `Hook` type with `EventDisposition::PassThrough`:

```rust
use openlogi_hook::{Hook, EventDisposition};

fn main() {
    // Start the hook; the closure receives every mouse/keyboard event.
    let hook = Hook::start(|event| {
        println!("Observed: {event:?}");
        // Forward the event unchanged.
        EventDisposition::PassThrough
    })
    .expect("failed to install hook");

    // … run your application …

    // Clean shutdown.
    hook.stop();
}

```

*Source:* [`openlogi-hook/examples/print_events.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-hook/examples/print_events.rs)

### Synthesizing Keyboard Shortcuts

To inject a keyboard shortcut corresponding to an `Action`, use the `execute` function:

```rust
use openlogi_core::binding::{Action, KeyCombo};
use openlogi_inject::execute;

// Build a simple Action that presses Cmd+Shift+S (macOS) / Ctrl+Shift+S (Linux/Windows).
let action = Action::KeyPress {
    combo: KeyCombo::new()
        .with_command(true)   // Cmd on macOS, Ctrl on others
        .with_shift(true)
        .with_key(openlogi_core::binding::KeyboardUsage::S),
};

// Dispatch the action to the OS.
execute(&action).expect("failed to inject event");

```

*Source:* [`openlogi-inject/src/inject.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-inject/src/inject.rs)

### Combining Capture and Injection

You can mix capture and injection to create gesture-based triggers. For example, detecting a drag gesture and synthesizing a click:

```rust
use openlogi_hook::{Hook, EventDisposition, MouseEvent};
use openlogi_inject::execute;
use openlogi_core::binding::Action;

let hook = Hook::start(|event| {
    match event {
        openlogi_hook::HookEvent::Mouse(MouseEvent::Moved { delta_x, delta_y }) => {
            // If the user drags rightwards > 100 pixels, synthesize a left click.
            if delta_x > 100 {
                let click = Action::MouseClick { 
                    button: openlogi_core::binding::ButtonId::Left 
                };
                execute(&click).ok();
            }
            EventDisposition::PassThrough
        }
        _ => EventDisposition::PassThrough,
    }
}).unwrap();

```

*Sources:* [`openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-hook/src/lib.rs) and [`openlogi-inject/src/inject.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-inject/src/inject.rs)

## Summary

- **`openlogi-hook`** captures raw OS input events using platform-specific taps (`CGEventTap`, `evdev`, `WH_MOUSE_LL`) and is part of the agent process.
- **`openlogi-inject`** synthesizes OS-level events from `Action` objects using `execute()`, enabling both the agent and core to send input without platform-specific knowledge.
- The hook crate depends on the inject crate for re-injection scenarios, while inject remains independent for testability.
- Both crates are documented in [`docs/DEVELOPMENT.md`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/DEVELOPMENT.md) as the *OS mouse hook* and *input synthesis* components respectively.

## Frequently Asked Questions

### Can I use openlogi-inject without openlogi-hook?

Yes. The `openlogi-inject` crate is fully independent and only requires the core types (`Action`, `KeyCombo`). You can use it to programmatically control the mouse and keyboard without capturing any input, making it suitable for automation scripts or testing environments that don't need the hook subsystem.

### Why are these crates separated instead of combined?

OpenLogi maintains this separation to keep the **core** logic platform-neutral and to enable safe unit testing. By isolating the injection layer from the capture layer, the core can test action-to-event translation without physical hardware or OS-specific I/O dependencies. The architecture also allows the agent to use both crates while the core uses only inject for simulations.

### Which crate handles the DPI cycle logic in OpenLogi?

The **DPI-cycle logic** resides in the agent process that uses `openlogi-hook`. While the hook captures the raw events that trigger DPI changes, the actual state management happens in the agent. The `openlogi-hook` crate provides the raw input data that flows to this logic, as shown in the component diagram in [`docs/DEVELOPMENT.md`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/DEVELOPMENT.md).

### How do the crates handle platform differences?

Each crate contains platform-specific submodules: [`macos.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/macos.rs), [`linux.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/linux.rs), and [`windows.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/windows.rs). For `openlogi-hook`, these implement the low-level tap mechanisms (`CGEventTap`, `evdev` grab, `WH_MOUSE_LL`). For `openlogi-inject`, these implement the synthesis APIs (`CGEvent`, `uinput`, `SendInput`). Both expose a unified public API that hides these platform details from the rest of the OpenLogi workspace.