What Is the openlogi‑inject Crate? Platform‑Specific Input Event Synthesis in OpenLogi

The openlogi‑inject crate is the platform‑specific input event synthesis layer of OpenLogi, responsible for converting high‑level Action definitions into real OS input events—keyboard strokes, mouse clicks, scroll wheels, and system commands—while keeping the rest of the codebase completely platform‑agnostic.

This crate serves as the I/O boundary of the OpenLogi system. It receives abstract actions from openlogi‑core and translates them into concrete input events using native platform APIs: CGEvent on macOS, uinput on Linux, and SendInput on Windows.

Core Responsibilities of openlogi‑inject

The crate implements several specialized functions that isolate OS‑specific complexity from the main logic:

Main Dispatch: execute(&Action)

The primary entry point execute() lives in crates/openlogi-inject/src/inject.rs. It uses a cfg_select! macro to dispatch to the appropriate backend:

  • macos::execute – macOS implementation
  • linux::execute – Linux uinput implementation
  • windows::execute – Windows SendInput implementation

This design allows openlogi‑core to define actions without any #[cfg] conditional compilation clutter.

Keyboard Chord Handling

Complex modifier combinations require careful state management to handle concurrent usage correctly:

  • press_hold(&KeyCombo) – Creates a synthetic held chord that stays active until the returned HeldChord is dropped
  • HeldChord – Implements reference‑counting for shared modifier keys across concurrent chords, ensuring modifiers release only when no chord still needs them
  • Shared state HELD_OUTPUT – Tracks which output keys are currently held to prevent double‑presses or premature releases

Scroll Event Injection

The crate handles both discrete and smooth scrolling:

Function Purpose
post_scroll(ScrollDelta) Injects immediate scroll events
post_smooth_scroll(ScrollDelta, SmoothScrollPhase) Handles animated scroll sequences across multiple frames
ScrollQuantizer Preserves high‑resolution deltas when quantizing for platforms that use wheel ticks

On macOS, scroll events use pixel‑precise CGEvent values. Linux and Windows receive quantized wheel tick values.

Synthetic Event Tagging

To prevent feedback loops where OpenLogi's own input hook captures events it just injected, the crate marks synthesized events with SYNTHETIC_EVENT_USER_DATA. The input hook checks this marker and ignores tagged events.

Browser Navigation Helper (macOS)

ax_navigate_browser(pid, forward) uses the macOS Accessibility API to programmatically click the "Back" or "Forward" toolbar buttons of the front‑most browser application.

Linux Debugging Support

action_device_path() returns the filesystem path of the uinput device node created for injection. This enables debugging with tools like evtest or direct monitoring via /dev/input/.

HID to Windows Keycode Translation

hid_usage_to_windows converts USB‑HID usage IDs to Windows virtual‑key codes, handling the mapping layer between standard HID identifiers and Windows‑specific representations.

Architectural Flow

The data flow through openlogi‑inject follows a clean separation pattern:

  1. Core creates an Action (e.g., KeyCombo::parse("Ctrl+Shift+A"))
  2. Core calls openlogi_inject::execute(&action)
  3. execute selects the correct backend via cfg_select!
  4. Backend synthesizes platform‑specific events
  5. For keyboard chords, backend coordinates via HELD_OUTPUT shared state
  6. Scroll gestures pass through ScrollQuantizer for frame‑accurate delivery

This keeps injection logic purely I/O while the agent, UI, and hook components focus on state management and user interaction.

Code Examples

Execute a Simple Action

use openlogi_core::binding::Action;
use openlogi_inject::execute;

let action: Action = "OpenApplication:/Applications/Calculator.app"
    .parse()
    .unwrap();

execute(&action);  // Dispatches to macOS, Linux, or Windows backend

Hold a Modifier Chord

use openlogi_inject::press_hold;

let combo = "Ctrl+Shift+Z".parse().unwrap();
let held = press_hold(&combo);  // Synthesizes key-down events

// ... while physical key remains pressed ...

// When `held` drops, key-up events emit automatically

Inject Scroll Events

use openlogi_inject::{post_scroll, post_smooth_scroll, SmoothScrollPhase};
use openlogi_core::scroll::ScrollDelta;

// Discrete scroll
post_scroll(ScrollDelta::wheel_ticks(0.0, 1.0));  // One tick down

// Smooth scroll animation
let delta = ScrollDelta::wheel_ticks(0.0, 0.125);
post_smooth_scroll(delta, SmoothScrollPhase::Began);
post_smooth_scroll(delta, SmoothScrollPhase::Changed);
post_smooth_scroll(delta, SmoothScrollPhase::Ended);

Linux Device Path Diagnostic

#[cfg(target_os = "linux")]
fn show_device_path() {
    if let Some(path) = openlogi_inject::action_device_path() {
        println!("Injector uinput device: {}", path.display());
    }
}

Key Source Files

File Role
crates/openlogi-inject/src/lib.rs Public API entry point, re‑exports execute, press_hold, post_scroll, etc.
crates/openlogi-inject/src/inject.rs Core injection logic, platform dispatch, and shared state management
crates/openlogi-inject/src/inject/macos.rs macOS implementation using CGEvent and Accessibility APIs
crates/openlogi-inject/src/inject/linux.rs Linux implementation via uinput virtual device
crates/openlogi-inject/src/inject/windows.rs Windows implementation using SendInput
crates/openlogi-inject/examples/inject_action.rs Stand‑alone CLI example demonstrating injector usage

Summary

  • openlogi‑inject is the sole I/O layer of OpenLogi, translating platform‑neutral Action types into native OS input events
  • Platform isolation is achieved through cfg_select! dispatch, keeping #[cfg] macros out of the core codebase
  • Keyboard chords use reference‑counted HeldChord and HELD_OUTPUT state for safe concurrent modifier sharing
  • Scroll injection supports both discrete events and smooth‑scroll animations with resolution preservation
  • Synthetic event tagging via SYNTHETIC_EVENT_USER_DATA prevents input hook feedback loops
  • macOS-specific Accessibility API integration enables browser navigation automation
  • Linux-specific action_device_path() aids debugging with standard input tools

Frequently Asked Questions

How does openlogi‑inject choose which platform backend to use?

The crate uses a cfg_select! macro at compile time in crates/openlogi-inject/src/inject.rs to select the appropriate implementation. This resolves to macos::execute, linux::execute, or windows::execute based on the target triple, with no runtime overhead.

Why is keyboard chord handling so complex in openlogi‑inject?

Multiple chords may share modifier keys (e.g., Ctrl+A and Ctrl+B both hold Ctrl). The HeldChord type and HELD_OUTPUT shared state implement reference counting so that Ctrl releases only when the last chord needing it drops. This prevents stuck keys or premature modifier release during concurrent operations.

Can I use openlogi‑inject independently of the full OpenLogi system?

Yes. The crates/openlogi-inject/examples/inject_action.rs example demonstrates standalone usage. The crate depends only on openlogi‑core for Action and ScrollDelta types, making it suitable for any Rust project needing cross‑platform input synthesis.

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 →