# How OpenLogi Implements OS Input Hook Across macOS, Linux, and Windows

> Discover how OpenLogi captures user input across macOS Linux and Windows using OS input hooks like CGEventTap evdev and WH_MOUSE_LL Understand the core mechanism behind cross platform input interception.

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

---

**OpenLogi uses platform-specific OS input hook implementations—CGEventTap on macOS, evdev+uinput on Linux, and WH_MOUSE_LL on Windows—to intercept raw mouse and keyboard events before they reach the desktop environment.**

The **OS input hook** in OpenLogi enables real-time interception of user input for per-application DPI and button remapping. Implemented in the `openlogi-hook` crate within the AprilNEA/OpenLogi repository, this cross-platform system translates native platform events into a unified `HookEvent` stream consumed by the higher-level agent.

## Cross-Platform Hook Architecture

All three backends expose a common interface defined in [`crates/openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/lib.rs). The `spawn()` function initializes the platform-specific hook and returns a stream of `HookEvent` structures:

```rust
/// Starts the appropriate OS‑specific hook and returns a stream of `HookEvent`s.
pub fn spawn() -> impl Stream<Item = HookEvent>;

```

## macOS: CGEventTap Implementation

On macOS, the hook leverages the **Core Graphics Event Tap** API to intercept HID-level events. The implementation resides in [`crates/openlogi-hook/src/macos.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/macos.rs).

The system creates a tap at the **HID** event source using `CGEventTapLocation::HID` with `HeadInsertEventTap` placement, ensuring events are intercepted before reaching applications. The closure receives a `CGEventTapProxy`, the `CGEventType`, and a reference to the `CGEvent`, which it translates into OpenLogi's internal `HookEvent` format.

A dedicated watchdog thread in [`crates/openlogi-hook/src/macos/watchdog.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/macos/watchdog.rs) monitors the tap status via `CGEventTapIsEnabled`. If the system disables the tap—for example, when Accessibility permissions are revoked—the watchdog automatically re-enables it to maintain continuous input capture.

### macOS Event Tap Code Example

```rust
let tap = CGEventTap::new(
    CGEventTapLocation::HID,
    CGEventTapPlacement::HeadInsertEventTap,
    CGEventTapOptions::Default,
    |_, etype, event| translate(etype, event),
)?;
tap.enable();               // `CGEventTapEnable` activates the tap

```

## Linux: evdev and uinput Implementation

The Linux backend in [`crates/openlogi-hook/src/linux.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/linux.rs) uses the **evdev** interface to grab exclusive access to physical input devices under `/dev/input/`. By calling `evdev::Device::grab()`, the process receives all kernel input reports directly, bypassing the usual event distribution.

For events that should not be blocked, the system creates a virtual **uinput** device (`VirtualDevice`) to re-emit the original event. This preserves system functionality while allowing OpenLogi to monitor or modify the data stream. The implementation filters out its own virtual devices by checking for the `UINPUT_PREFIX` name prefix to prevent recursive event loops.

### Linux Device Grabbing Example

```rust
let dev = evdev::Device::open("/dev/input/event3")?.grab()?;
let mut vdev = build_virtual_device(&dev)?;
for ev in dev.fetch_events()? {
    let hook_evt = translate_evdev_event(ev);
    if !hook_evt.is_suppressed {
        vdev.emit(&ev)?;   // pass‑through
    }
}

```

## Windows: WH_MOUSE_LL Low-Level Hook

On Windows, the hook utilizes the `WH_MOUSE_LL` and `WH_KEYBOARD_LL` low-level hook types via the Win32 API. The implementation in [`crates/openlogi-hook/src/windows.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/windows.rs) registers callbacks using `SetWindowsHookExW`.

The `mouse_hook_proc` callback receives a `MSLLHOOKSTRUCT` pointer containing raw mouse coordinates, button states, and wheel data for every input event. After translation into `HookEvent` structures, the callback returns `CallNextHookEx` for unhandled events, allowing standard Windows message processing to continue. A helper thread maintains the message pump using `GetMessageW` to keep the hook active.

### Windows Low-Level Hook Example

```rust
unsafe extern "system" fn mouse_hook_proc(
    code: i32,
    wparam: WPARAM,
    lparam: LPARAM,
) -> LRESULT {
    if code == HC_ACTION {
        let data = *(lparam as *const MSLLHOOKSTRUCT);
        let ev = translate_mouse(data);
        // … process ev …
    }
    CallNextHookEx(ptr::null_mut(), code, wparam, lparam)
}
SetWindowsHookExW(WH_MOUSE_LL, Some(mouse_hook_proc), HINSTANCE::default(), 0);

```

## Summary

- OpenLogi implements **OS input hook** functionality through three platform-specific backends: CGEventTap for macOS, evdev+uinput for Linux, and WH_MOUSE_LL for Windows.
- All backends translate native events into a unified `HookEvent` stream via the common `spawn()` API in [`crates/openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/lib.rs).
- The macOS implementation includes a watchdog mechanism in [`watchdog.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/watchdog.rs) to automatically re-enable the event tap if system permissions change.
- Linux uses exclusive device grabbing with evdev and virtual uinput devices to maintain system functionality while monitoring inputs.
- Windows employs low-level global hooks with proper message pumping to capture mouse and keyboard events system-wide.

## Frequently Asked Questions

### What is an OS input hook and why does OpenLogi need one?

An OS input hook is a mechanism that intercepts raw hardware input events before they reach the operating system's standard event distribution. OpenLogi requires this capability to implement per-application DPI scaling and button remapping by monitoring and optionally modifying mouse and keyboard events in real-time before applications receive them.

### Why does the macOS implementation need a watchdog thread?

The macOS watchdog thread, implemented in [`crates/openlogi-hook/src/macos/watchdog.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/macos/watchdog.rs), periodically checks `CGEventTapIsEnabled` because macOS automatically disables event taps when Accessibility permissions are revoked or when system security policies change. The watchdog re-enables the tap automatically to ensure continuous input monitoring without requiring manual intervention.

### How does the Linux backend prevent blocking system input when grabbing devices?

The Linux implementation uses a **uinput** virtual device to re-emit events that should not be suppressed. After grabbing the physical device with `evdev::Device::grab()`, it translates events into `HookEvent` structures, then passes non-suppressed events back to the system through the virtual device, maintaining normal desktop functionality while still monitoring all inputs.

### What permissions are required for these OS input hooks to function?

macOS requires **Accessibility** permissions for `CGEventTap` to capture system-wide input events. Linux requires read/write access to `/dev/input/` device nodes and the ability to create uinput devices, typically requiring membership in the `input` group or elevated privileges. Windows requires the application to run with standard user privileges, though some environments may require elevation depending on security policies.