OpenLogi Platform-Specific Input Hook Implementation: Cross-Platform Event Capture Architecture

OpenLogi captures raw mouse and keyboard events across macOS, Windows, and Linux through a unified Rust API that abstracts CGEventTap, WinAPI low-level hooks, and evdev into a single channel-based event stream.

The AprilNEA/OpenLogi repository implements a sophisticated input interception layer that enables low-latency device control across operating systems. This OpenLogi platform-specific input hook implementation consolidates disparate OS mechanisms into the openlogi-hook crate, allowing the agent core and overlay components to consume normalized input events without platform-specific dependencies.

Architectural Overview of the Hook Layer

macOS: CGEventTap Implementation

On macOS, the implementation in crates/openlogi-hook/src/macos.rs utilizes CGEventTap to intercept hardware events at the system level before they reach foreground applications. Because macOS may terminate event taps when the user switches applications or under power management, a dedicated watchdog in watchdog.rs monitors the tap's health and automatically restarts it if the system invalidates the connection.

Windows: Low-Level WinAPI Hooks

The Windows backend in crates/openlogi-hook/src/windows.rs registers WH_MOUSE_LL and WH_KEYBOARD_LL low-level hooks through the WinAPI, capturing input within the same process before distribution to other windows. Events flow through a thread-safe channel implemented with crossbeam, ensuring lock-free communication between the hook thread and the consumer.

Linux: evdev and Desktop Environment Helpers

Linux systems use crates/openlogi-hook/src/linux.rs to read raw events directly from evdev devices in /dev/input/*, while synthetic actions are injected via uinput. To handle compositor-specific behaviors on modern desktops, the crate includes crates/openlogi-hook/src/linux/gnome_shell.rs for GNOME Shell integration and crates/openlogi-hook/src/linux/wlr_foreign_toplevel.rs for Wayland window management under wlroots-based compositors.

Core API Design in openlogi-hook

All three implementations expose a uniform interface defined in crates/openlogi-hook/src/lib.rs. The public API centers on three primary methods:

  • Hook::new() instantiates the platform-appropriate backend.
  • Hook::start() spawns a background thread to begin event capture.
  • Hook::receiver() returns a crossbeam::channel::Receiver<Event> that downstream components poll for normalized input data.

This design encapsulates platform differences behind a single crate boundary. Downstream modules in openlogi-agent-core and openlogi-overlay interact solely with the Event enum and channel receiver, remaining ignorant of whether the underlying source is CGEventTap or evdev.

Platform-Specific Compilation with cfg Gates

The crate uses conditional compilation to include only the relevant implementation for the target operating system. As implemented in AprilNEA/OpenLogi, the module hierarchy in crates/openlogi-hook/src/lib.rs applies #[cfg(target_os = "macos")], #[cfg(target_os = "windows")], and #[cfg(target_os = "linux")] attributes to gate the platform modules.

This ensures that Windows-specific WinAPI calls are not compiled on Linux, and that macOS-specific Core Graphics frameworks are excluded from non-Apple builds. The strategy follows the cross-platform rules outlined in .claude/rules/cross-platform.md, guaranteeing that modifications to the hook's public contract trigger recompilation of all dependent crates across every supported platform.

Integration with the OpenLogi Agent and Overlay

The hook runs within the agent process, which possesses the necessary privileges to access low-level input APIs. When openlogi-agent-core initializes, it creates the hook via openlogi_hook::Hook::new() and forwards received events to src/event_monitor.rs.

The event monitor normalizes raw platform events into OpenLogi's internal InputEvent type, which the action ring (src/action_ring.rs) processes to trigger DPI adjustments, button remaps, or custom macros. Separately, the overlay (openlogi-overlay) subscribes to the same crossbeam channel, enabling visual feedback such as the Actions Ring without duplicating hook logic or requiring elevated permissions in the GUI process.

Minimal Usage Example

The following Rust code demonstrates how to instantiate and consume events from the hook:

use openlogi_hook::{Hook, Event};

fn main() -> anyhow::Result<()> {
    // Create the platform-specific hook (macOS, Windows, or Linux)
    let hook = Hook::new()?;
    
    // Start capturing events in a background thread
    hook.start()?;

    // Receive normalized events from the channel
    let rx = hook.receiver();
    while let Ok(event) = rx.recv() {
        match event {
            Event::MouseMove { dx, dy } => println!("Delta: {dx}, {dy}"),
            Event::KeyPress { keycode } => println!("Key: {keycode}"),
            _ => {}
        }
    }
    Ok(())
}

This identical source compiles and executes across all supported platforms because Hook::new() automatically selects the appropriate backend implementation at compile time.

Summary

  • OpenLogi consolidates macOS CGEventTap, Windows low-level hooks, and Linux evdev into a single crate (openlogi-hook) with a unified API.
  • The implementation uses #[cfg] gates to compile only platform-relevant code, ensuring clean separation and minimal binary size.
  • Events flow through a crossbeam channel, allowing the agent core, event monitor, action ring, and overlay to consume input without platform-specific dependencies.
  • macOS requires a watchdog mechanism to handle system-initiated tap terminations, while Linux requires separate helpers for GNOME and Wayland compositor integration.

Frequently Asked Questions

How does OpenLogi capture input without platform-specific code in the main application?

The openlogi-hook crate abstracts OS-specific mechanisms behind a common Hook struct and Event enum. Downstream crates like openlogi-agent-core call Hook::new() and receive a crossbeam::channel::Receiver<Event>, allowing them to process mouse and keyboard data without importing CGEventTap, WinAPI, or evdev directly.

What mechanisms prevent the macOS event tap from being terminated by the OS?

The macOS implementation includes a watchdog module that monitors the CGEventTap's health. If the system invalidates the tap—commonly occurring during application switching or power transitions—the watchdog detects the failure and automatically respawns the tap to maintain continuous input capture.

Why does the Linux implementation require both evdev and uinput?

The Linux backend uses evdev to read raw hardware events from /dev/input/* devices, providing unfiltered access to mouse and keyboard data. It uses uinput to inject synthetic events, enabling features like remapped buttons or DPI shifts that require the operating system to perceive hardware-generated input.

Which components consume the event stream from openlogi-hook?

Three primary consumers receive the channel receiver: the event monitor (src/event_monitor.rs) for normalization, the action ring (src/action_ring.rs) for executing remaps and macros, and the overlay (openlogi-overlay) for rendering visual feedback like the Actions Ring.

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 →