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

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. The spawn() function initializes the platform-specific hook and returns a stream of HookEvent structures:

/// 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.

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 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

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 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

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 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

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.
  • The macOS implementation includes a watchdog mechanism in 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, 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.

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 →