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 implementationlinux::execute– Linux uinput implementationwindows::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 returnedHeldChordis droppedHeldChord– 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:
- Core creates an
Action(e.g.,KeyCombo::parse("Ctrl+Shift+A")) - Core calls
openlogi_inject::execute(&action) executeselects the correct backend viacfg_select!- Backend synthesizes platform‑specific events
- For keyboard chords, backend coordinates via
HELD_OUTPUTshared state - Scroll gestures pass through
ScrollQuantizerfor 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
Actiontypes 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
HeldChordandHELD_OUTPUTstate 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_DATAprevents 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →