# Platform-specific Input Handling in OpenLogi: macOS, Linux, and Windows Implementation

> Master platform-specific input handling in OpenLogi across macOS, Linux, and Windows. Discover how HookBackend unifies OS-native mechanisms for seamless input capture and synthesis.

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

---

**OpenLogi unifies cross-platform input capture and synthesis through a `HookBackend` trait that abstracts OS-native mechanisms including CGEventTap on macOS, evdev/uinput on Linux, and low-level Windows hooks.**

OpenLogi implements a sophisticated input pipeline that bridges operating system differences while respecting platform security models. The `openlogi-hook` crate defines a common interface that enables consistent **platform-specific input handling** across macOS, Linux, and Windows without sacrificing low-level control. This article examines the architecture in the AprilNEA/OpenLogi repository, detailing how each backend captures hardware events and injects synthetic input.

## The HookBackend Trait Abstraction

At the core of OpenLogi's input system lies the `HookBackend` trait defined in [`crates/openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/lib.rs). This trait establishes a contract for platform-specific implementations, exposing methods for event iteration and input injection. The factory function `HookBackend::new()` selects the appropriate concrete backend at compile time, returning a unified interface regardless of the target operating system.

All backends implement the same event model using structs like `HookEvent`, `MouseEvent`, and `KeyEvent`. This design allows higher-level application code to process input uniformly while the underlying platform handles OS-specific nuances.

## Platform-Specific Backend Implementations

### macOS: CGEventTap and Core Graphics

The macOS backend in [`crates/openlogi-hook/src/macos.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/macos.rs) utilizes **CGEventTap** from Apple's Core Graphics framework. The implementation spawns a private thread running a `CFRunLoop` to host the event tap, which intercepts mouse and keyboard events at the HID (Human Interface Device) level.

To identify specific hardware sources, the code calls `CGEventCopyIOHIDEvent` and `IOHIDEventGetSenderID` to extract the originating device identifier. The backend includes a watchdog mechanism that monitors the tap lifecycle, automatically re-enabling it if macOS disables the tap during system sleep or permission changes. Users must grant Accessibility permissions, which the code verifies using `AXIsProcessTrusted`.

### Linux: evdev and uinput Integration

The Linux implementation in [`crates/openlogi-hook/src/linux.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/linux.rs) employs the **evdev** kernel interface to grab physical mouse devices and **uinput** to re-inject synthetic events. The backend enumerates available input devices using `evdev::enumerate()`, then claims exclusive access to relevant hardware.

For output, the system constructs a virtual device via `build_virtual_device` and translates raw `evdev::InputEvent` structures into the common `MouseEvent` and `KeyEvent` abstractions. The implementation also supports high-resolution scrolling wheels. Unlike macOS, this backend requires no special user-grantable permissions beyond standard device file access for `/dev/input/*` nodes.

### Windows: Low-Level Hook APIs

The Windows backend in [`crates/openlogi-hook/src/windows.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/windows.rs) installs **WH_MOUSE_LL** and **WH_KEYBOARD_LL** hooks via `SetWindowsHookExW`. These low-level hooks run on a dedicated thread that pumps a Windows message loop, capturing input before it reaches target applications.

For synthetic input generation, the backend uses the Win32 **SendInput** API. The implementation can distinguish between physical and synthetic events by examining parameters passed to the hook callbacks, preventing infinite loops when injecting events. No additional privacy permissions are required beyond standard user privileges.

## Cross-Platform Event Model

All three backends translate native OS events into a shared representation. The `HookEvent` enum variants wrap platform-specific data into `MouseEvent` and `KeyEvent` structs containing normalized fields like position coordinates, key states, and device identifiers. This abstraction ensures that calling code in [`crates/openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/lib.rs) remains agnostic to the underlying platform while receiving rich, structured input data.

## Implementing Input Capture and Injection

### Initializing the Platform Backend

The following example demonstrates starting a hook and processing events across all supported platforms:

```rust
use openlogi_hook::{HookBackend, HookEvent, HookError};

fn main() -> Result<(), HookError> {
    // Create the platform-specific backend; the concrete type is selected at compile time
    let mut backend = HookBackend::new()?;

    // Run the event loop; each iteration yields a high-level HookEvent
    while let Some(event) = backend.next_event()? {
        match event {
            HookEvent::Mouse(m) => println!("mouse: {:?} at {:?}", m.kind, m.position),
            HookEvent::Key(k)   => println!("key:   {:?} {}", k.kind, k.is_down),
        }
    }
    Ok(())
}

```

### Synthesizing macOS Input Events

For injecting keyboard shortcuts on macOS, the implementation uses Core Graphics events:

```rust
use openlogi_inject::inject::Inject;
use core_graphics::event::{CGEvent, CGEventFlags, CGEventTapLocation};

fn send_cmd_shift_c() {
    // Create a source for HID events
    let src = core_graphics::event_source::CGEventSource::new(
        core_graphics::event_source::CGEventSourceStateID::HIDSystemState,
    ).unwrap();

    // Command + Shift + C (keycode 8)
    let mut ev = CGEvent::new_keyboard_event(&src, 8, true).unwrap();
    ev.set_flags(CGEventFlags::CGEventFlagCommand | CGEventFlags::CGEventFlagShift);
    ev.post(CGEventTapLocation::HID);

    // Release the keys
    let mut ev_up = CGEvent::new_keyboard_event(&src, 8, false).unwrap();
    ev_up.set_flags(CGEventFlags::CGEventFlagCommand | CGEventFlags::CGEventFlagShift);
    ev_up.post(CGEventTapLocation::HID);
}

```

### Injecting Linux Input Events

On Linux, synthetic input utilizes the evdev and uinput interfaces:

```rust
use openlogi_inject::inject::Inject;
use evdev::{InputEvent, InputEventKind};

fn click_left() {
    // Build a synthetic button press
    let press = InputEvent::new(
        evdev::EventType::KEY,
        evdev::KeyCode::BTN_LEFT.0,
        InputEventKind::Key(evdev::KeyState::Pressed),
    );
    
    // Followed by a release
    let release = InputEvent::new(
        evdev::EventType::KEY,
        evdev::KeyCode::BTN_LEFT.0,
        InputEventKind::Key(evdev::KeyState::Released),
    );

    // Forward these to the uinput device
    Inject::inject_linux(&[press, release]).unwrap();
}

```

## Summary

- OpenLogi uses a trait-based abstraction in [`crates/openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/lib.rs) to unify platform-specific input handling across operating systems.
- The **macOS** backend leverages CGEventTap with Core Graphics APIs and requires Accessibility permissions verified via `AXIsProcessTrusted`.
- The **Linux** implementation utilizes evdev for hardware capture and uinput for injection, requiring only standard device file access without elevated privileges.
- The **Windows** backend employs WH_MOUSE_LL and WH_KEYBOARD_LL hooks with SendInput for synthetic event generation and differentiates physical from synthetic input.
- All platforms share common event structs (`HookEvent`, `MouseEvent`, `KeyEvent`) for consistent higher-level processing regardless of OS.

## Frequently Asked Questions

### Does OpenLogi require root privileges on Linux?

No, OpenLogi on Linux only requires standard device file access permissions for evdev and uinput nodes. While some distributions may require adding the user to the `input` group, the implementation does not require full root privileges to capture or synthesize events.

### How does the macOS backend handle system sleep and accessibility permission changes?

The macOS implementation includes a watchdog mechanism that monitors the CGEventTap lifecycle. If the system disables the tap during sleep or due to permission revocation, the code automatically re-enables the tap to ensure continuous input handling without requiring application restart.

### Can the Windows backend distinguish between physical and synthetic input events?

Yes, the Windows implementation in [`crates/openlogi-hook/src/windows.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/windows.rs) uses hook callback parameters to differentiate between real hardware events and synthetic events generated via the SendInput API. This prevents the system from processing its own injected input as new raw events.

### What Rust trait enables cross-platform input handling in OpenLogi?

The `HookBackend` trait defined in [`crates/openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/lib.rs) provides the common interface for all platforms. The factory method `HookBackend::new()` selects the appropriate concrete implementation at compile time, enabling zero-cost abstraction across macOS, Linux, and Windows.