# How OpenLogi Synthesizes Input Using CGEvent, uinput, and SendInput to Replay Remapped Actions

> Learn how OpenLogi synthesizes input using CGEvent, uinput, and SendInput to replay remapped actions with sub-pixel precision across macOS, Linux, and Windows.

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

---

**OpenLogi synthesizes native OS input events by translating abstract `Action` types into platform-specific APIs—CGEvent on macOS, uinput on Linux, and SendInput on Windows—to replay remapped actions with sub-pixel precision.**

OpenLogi's input injection pipeline lives in the **`openlogi-inject`** crate, which serves as the bridge between high-level gesture remapping logic and low-level operating system input queues. When the GUI or a gesture engine triggers a remapped action, the system calls `execute()` with an `Action` from `openlogi_core::binding::Action`, dispatching to platform-specific backends that synthesize hardware-level events.

## The Central Dispatch Architecture

The synthesis pipeline begins in [`crates/openlogi-inject/src/inject.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-inject/src/inject.rs) with the `execute` function:

```rust
pub fn execute(action: &Action) { … }

```

This function handles special cases (like opening applications) and then routes to the appropriate backend using the `cfg_select!` macro:

```rust
cfg_select! {
    target_os = "macos" => macos::execute(action),
    target_os = "linux" => linux::execute(action),
    target_os = "windows" => windows::execute(action),
    _ => { … }
}

```

Each backend translates the abstract `Action` into native OS event structures, ensuring consistent behavior across macOS, Linux, and Windows.

## macOS: CGEvent Synthesis

On macOS, the implementation in [`crates/openlogi-inject/src/inject/macos.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-inject/src/inject/macos.rs) leverages **Core Graphics** (`core_graphics` crate) to create synthetic hardware events.

### Event Source Creation

The system initializes a **CGEventSource** with `CGEventSourceStateID::HIDSystemState` to mirror hardware input states. When generating events, the code creates **CGEvent** objects and posts them to the system event tap.

### Handling Different Action Types

The `macos::execute` function classifies actions into `Effect` types and dispatches to specialized helpers:

- **Mouse clicks**: `post_click` generates paired mouse-down and mouse-up events
- **Keyboard chords**: `post_keycombo` builds virtual key codes via `hid_usage_to_macos` and flag masks via `combo_flags`, then posts down/up phases through `post_key_phase`
- **Scroll gestures**: `dispatch_scroll` uses `CGEvent::new_scroll_event` with quantization helpers to preserve sub-pixel precision
- **Media keys**: `post_media_key` handles system media controls
- **Window manager actions**: Mission Control and Dock commands use the private `CoreDockSendNotification` SPI

### Preventing Feedback Loops

Critical to the implementation, synthetic events are tagged with a unique user-data constant **`SYNTHETIC_EVENT_USER_DATA`**. OpenLogi's own event tap checks for this marker to ignore self-generated events, preventing infinite recursion when monitoring global input.

## Linux: uinput Virtual Device

The Linux backend in [`crates/openlogi-inject/src/inject/linux.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-inject/src/inject/linux.rs) utilizes the **uinput** subsystem to create a virtual input device.

### Device Initialization

The system lazy-initializes a `/dev/uinput` device node via `linux::device_node()`, obtaining a file descriptor for emitting kernel input events.

### Event Emission

Linux input events are structured using standard event types:

- **Key events**: `EV_KEY` for keyboard keys and mouse buttons
- **Relative motion**: `EV_REL` for mouse movement and scroll wheels (`REL_WHEEL`, `REL_HWHEEL`)
- **Absolute positioning**: `EV_ABS` where required by the input subsystem

Scroll gestures apply the same quantization logic used on macOS, ensuring consistent scroll distances across platforms when replaying remapped actions.

## Windows: SendInput Implementation

On Windows, [`crates/openlogi-inject/src/inject/windows.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-inject/src/inject/windows.rs) utilizes the Win32 **`SendInput`** API to inject events into the system input queue.

### INPUT Structure Construction

The `windows::execute` function builds arrays of **`INPUT`** structures containing:

- **Keyboard input**: Translated via `hid_usage_to_windows` (mapping table defined in [`inject.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/inject.rs)), using `KEYBDINPUT` structures with virtual-key codes
- **Mouse input**: Constructed with `MOUSEINPUT` structures, utilizing flags like `MOUSEEVENTF_LEFTDOWN`, `MOUSEEVENTF_LEFTUP`, `MOUSEEVENTF_RIGHTDOWN`, etc.

### Atomic Injection

The entire array of `INPUT` structures passes atomically to `SendInput`, ensuring that complex chord sequences (like "Win + D") inject as a single transaction rather than interleaved with physical hardware events.

## Practical Code Examples

### Replaying a Remapped Shortcut on macOS

```rust
use openlogi_core::binding::{Action, Shortcut};

let action = Action::from(Shortcut::Copy);
openlogi_inject::execute(&action);
// Injects native CGEvent for Command+C

```

### Injecting a Mouse Button on Linux

```rust
use openlogi_core::binding::{Action, MouseButton};

let action = Action::from(MouseButton::Back);
openlogi_inject::execute(&action);
// Writes BTN_SIDE event to /dev/uinput

```

### Sending a Windows Native Action

```rust
use openlogi_core::binding::NativeAction;

let action = Action::from(NativeAction::ShowDesktop);
openlogi_inject::execute(&action);
// Calls SendInput with VK_LWIN + 'D' chord

```

## Summary

- **OpenLogi** uses the `openlogi-inject` crate to abstract platform-specific input APIs behind a unified `Action` interface.
- **macOS** leverages **CGEvent** with **CGEventSource**, tagging synthetic events with `SYNTHETIC_EVENT_USER_DATA` to prevent feedback loops.
- **Linux** employs **uinput** virtual devices emitting `EV_KEY` and `EV_REL` events through a lazy-initialized `/dev/uinput` node.
- **Windows** utilizes **`SendInput`** with arrays of `INPUT` structures to atomically inject keyboard and mouse events.
- All platforms share quantization logic for scroll gestures, ensuring consistent behavior when replaying remapped actions across operating systems.

## Frequently Asked Questions

### How does OpenLogi prevent infinite loops when synthesizing input?

OpenLogi tags every synthetic event with a unique constant **`SYNTHETIC_EVENT_USER_DATA`** when creating **CGEvent** objects on macOS. The system's own event tap checks for this marker and ignores flagged events, ensuring that injected input does not trigger the gesture engine recursively. This feedback prevention is critical for stable remapping of system shortcuts.

### What abstraction layer converts high-level gestures into OS-specific input?

The **`openlogi_core::binding::Action`** type serves as the hardware-agnostic intermediate representation. Defined in [`crates/openlogi-core/src/binding.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/binding.rs), this enum encompasses shortcuts, mouse buttons, media keys, and native window manager commands. The `openlogi_inject::execute` function consumes these `Action` types and dispatches to platform-specific backends that handle **CGEvent**, **uinput**, and **SendInput** translation.

### Why does OpenLogi use SendInput on Windows instead of keybd_event?

**`SendInput`** supersedes the legacy `keybd_event` and `mouse_event` APIs by injecting input at a lower level of the system input queue. Unlike the older functions, `SendInput` accepts an array of `INPUT` structures that inject atomically, preventing race conditions with physical hardware. This ensures that complex remapped chords (such as "Alt+Tab" or "Win+D") execute as coherent sequences rather than interleaved with user keypresses.

### How does OpenLogi ensure consistent scroll behavior across macOS and Linux?

Both platforms implement shared **quantization helpers** that convert high-level scroll distances into platform-specific units while preserving sub-pixel precision. On macOS, this involves `CGEvent::new_scroll_event` with calibrated wheel units; on Linux, the system emits `EV_REL` events with `REL_WHEEL` values using the same quantization algorithm. This unified approach ensures that remapped scroll gestures feel identical regardless of the underlying operating system.