How OpenLogi Synthesizes Input Events Across macOS, Linux, and Windows
OpenLogi synthesizes input events by separating platform-neutral action definitions in openlogi-core from OS-specific injection logic in the openlogi-inject crate, dispatching to CoreGraphics APIs on macOS, uinput on Linux, and Win32 SendInput on Windows.
OpenLogi is an open-source input automation framework that enables cross-platform event injection through a unified Rust API. The system achieves true portability by maintaining a strict architectural boundary between high-level user intentions and low-level OS driver interactions. This article examines how the AprilNEA/OpenLogi repository implements input synthesis across the three major desktop platforms.
Architecture Overview
OpenLogi structures its input pipeline into two distinct layers. The openlogi-core crate defines platform-agnostic data structures like Action and Effect, while the openlogi-inject crate handles the actual OS-level event emission.
The entry point for all injection operations is the openlogi_inject::execute function defined in crates/openlogi-inject/src/inject.rs. This function uses compile-time conditional compilation to select the appropriate platform backend:
// crates/openlogi-inject/src/inject.rs
#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "windows")]
mod windows;
pub fn execute(action: &Action) {
#[cfg(target_os = "macos")]
macos::execute(action);
#[cfg(target_os = "linux")]
linux::execute(action);
#[cfg(target_os = "windows")]
windows::execute(action);
}
Each platform module translates the generic Effect variants into native API calls using the OS-specific input subsystems.
Platform-Specific Implementation Details
macOS: CoreGraphics and Private SPIs
On macOS, OpenLogi synthesizes input events through the CoreGraphics framework, specifically utilizing CGEvent APIs for standard input and private Dock SPIs for window management actions. The implementation resides in crates/openlogi-inject/src/inject/macos.rs.
- Keyboard Events: The system calls
CGEvent::new_keyboard_eventwith virtual key codes derived from the US keyboard layout, combining them with modifier flags constructed viaCGEventFlags. - Unicode Text: For layout-independent text entry, the code invokes
CGEventKeyboardSetUnicodeStringto post arbitrary Unicode characters directly. - Mouse Input: Mouse clicks use
CGEvent::new_mouse_eventwithCGMouseButtonconstants (left, right, center) andOtherMousevariants for back/forward buttons. - Media Keys: These map to IOKit
NX_KEYTYPE_*constants and post viaCGEventCreateKeyboardEvent. - Window Manager: Private functions like
CoreDockSendNotificationtrigger Mission Control and App Exposé through undocumented Dock SPIs.
Linux: Virtual Uinput Devices
The Linux implementation in crates/openlogi-inject/src/inject/linux.rs creates a virtual input device using the uinput kernel subsystem. This approach writes raw input_event structures directly to /dev/input/eventN, appearing to the system as a standard HID device.
- Device Initialization: A lazy singleton (
static DEVICE: LazyLock<Mutex<UinputDevice>>) maintains a persistent virtual device handle. - Keyboard Synthesis: Key presses emit as
EV_KEYevents with values 1 (press) and 0 (release), translating macOSkVK_*codes to Linux key codes via the internallinux::keycodemapping. - Mouse Buttons: Physical button states use
BTN_LEFT,BTN_RIGHT, andBTN_MIDDLEevent codes. - Scroll Events: Horizontal and vertical scrolling quantizes delta values into
EV_RELevents withREL_HWHEELandREL_WHEELaxes.
Windows: Win32 SendInput API
On Windows, OpenLogi utilizes the SendInput Win32 API to synthesize input, as implemented in crates/openlogi-inject/src/inject/windows.rs. This API allows atomic injection of keyboard and mouse events by populating arrays of INPUT structures.
- Keyboard Injection: The system builds
INPUT_KEYBOARDstructures, mapping macOSkVK_*virtual key codes to WindowsVK_*equivalents. The Cmd key maps to Ctrl for cross-platform consistency. - Mouse Injection: Mouse clicks use
MOUSEEVENTF_LEFTDOWN,MOUSEEVENTF_LEFTUP,MOUSEEVENTF_RIGHTDOWN, etc. Wheel events utilizeMOUSEEVENTF_WHEELandMOUSEEVENTF_HWHEELfor vertical and horizontal scrolling. - Input Arrays: Multiple events are batched into a single
SendInputcall to ensure atomic delivery.
The Platform-Neutral Core
The abstraction that enables cross-platform synthesis lives in crates/openlogi-core/src/binding/effect.rs. The Effect enum defines all possible input operations without referencing any OS-specific types:
// crates/openlogi-core/src/binding/effect.rs
pub enum Effect {
Click(MouseButton),
Shortcut(Shortcut),
Key(KeyCombo),
HeldKey(KeyCombo),
Scroll { dx: i32, dy: i32 },
Media(MediaKey),
Native(NativeAction),
Script(Script<'_>),
Text(&'static str),
AgentSide,
None,
}
Platform modules pattern-match on these variants. For example, a Text effect invokes CGEventKeyboardSetUnicodeString on macOS, writes a sequence of EV_KEY events on Linux, and sends INPUT_KEYBOARD events on Windows. This design allows the same high-level configuration to execute natively across all supported operating systems.
Example: Injecting a Cross-Platform Shortcut
The following example demonstrates how client code initiates input synthesis without platform-specific knowledge:
use openlogi_core::binding::{Action, Shortcut};
use openlogi_inject::execute;
// Define a copy action (Cmd+C on macOS, Ctrl+C on Linux/Windows)
let shortcut = Shortcut::Copy;
let action = Action::new(shortcut);
// Execute platform-native injection
execute(&action);
When this code runs on macOS, execute dispatches to macos.rs and emits a Command+C via CoreGraphics. On Linux, the same call writes Ctrl+C keycodes to the uinput device. On Windows, it calls SendInput with VK_CONTROL and VK_C structures.
Summary
- OpenLogi separates action definitions (
openlogi-core) from injection mechanics (openlogi-inject) to maintain cross-platform compatibility. - macOS implementations use CoreGraphics
CGEventAPIs and private Dock SPIs for window management. - Linux creates a virtual uinput device at
/dev/input/eventNto emitEV_KEYandEV_RELevents. - Windows utilizes the Win32
SendInputAPI withINPUTstructures for keyboard and mouse synthesis. - The
Effectenum ineffect.rsprovides a platform-neutral bridge that each OS module translates into native API calls.
Frequently Asked Questions
What API does OpenLogi use for input injection on macOS?
OpenLogi uses Apple's CoreGraphics framework, specifically CGEventCreateKeyboardEvent and CGEventCreateMouseEvent for standard input. For window management features like Mission Control, it calls private SPIs such as CoreDockSendNotification defined in Dock private headers.
How does OpenLogi handle different keyboard layouts when synthesizing text?
On macOS, the framework calls CGEventKeyboardSetUnicodeString to inject Unicode characters directly, bypassing keyboard layout dependencies. On Linux and Windows, the platform modules map logical key identities to physical scan codes or virtual key codes, though the exact layout handling depends on the target system's configuration.
Can OpenLogi synthesize media key events like play or pause?
Yes. The Effect::Media variant supports media keys across all platforms. On macOS, these map to IOKit NX_KEYTYPE_PLAY, NX_KEYTYPE_NEXT, and related constants. The Linux and Windows implementations emit the appropriate key codes for their respective media control standards.
Where does the platform detection logic reside in the codebase?
Platform selection occurs at compile time in crates/openlogi-inject/src/inject.rs using #[cfg(target_os = "...")] attributes. The execute function statically dispatches to macos::execute, linux::execute, or windows::execute based on the target triple, eliminating runtime overhead for platform detection.
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 →