# How OpenLogi Synthesizes Input Events on macOS, Linux, and Windows

> Discover how OpenLogi synthesizes input events across macOS, Linux, and Windows using the openlogi-inject crate, translating actions to native OS API calls for seamless integration.

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

---

**OpenLogi delegates cross-platform input event synthesis to the `openlogi-inject` crate, which translates abstract actions into native OS API calls—Core Graphics on macOS, uinput on Linux, and SendInput on Windows.**

The AprilNEA/OpenLogi project implements a unified input remapping system that requires robust **synthesizing input events** across heterogeneous platforms. By isolating platform-specific generation logic into dedicated modules within the `openlogi-inject` crate, the codebase maintains clean separation between core binding logic and low-level system interaction.

## macOS: Core Graphics Event Injection

On macOS, the implementation resides in **[`crates/openlogi-inject/src/inject/macos.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-inject/src/inject/macos.rs)**. This module leverages the **Core Graphics** framework through the `core_graphics` crate to construct and post `CGEvent` structures directly to the window server.

### Keyboard and Media Key Synthesis

Standard key events are generated using **`CGEvent::new_keyboard_event`** with virtual-key codes and modifier flags (`CGEventFlags`). For media keys—including volume controls and play/pause—the code utilizes special NX key constants (`NX_KEYTYPE_*`) that map to macOS system media controls.

### Mouse and Scroll Events

Mouse clicks require creating paired mouse-down and mouse-up events using **`CGEvent::new_mouse_event`**, posted at the current cursor location via `CGEventTapLocation::HID`. Scroll events support three quantization strategies through dedicated **`ScrollQuantizer`** instances—`LINE_SCROLL_QUANTIZER`, `PIXEL_SCROLL_QUANTIZER`, and `SMOOTH_SCROLL_QUANTIZER`—which convert abstract deltas into `CGEventType::ScrollWheel` events with custom phase constants (`SCROLL_PHASE`, `MOMENTUM_PHASE`).

### Native System Actions

Functions such as **`mission_control()`**, **`show_desktop()`**, and **`sleep_system()`** invoke private macOS SPIs (System Programming Interfaces) or external utilities like `pmset`, as no public CGEvent types exist for these workspace-level actions.

## Linux: Virtual uinput Devices

Linux synthesis is handled in **[`crates/openlogi-inject/src/inject/linux.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-inject/src/inject/linux.rs)** using the **uinput** subsystem. The crate creates a virtual input device (via the `uinput` crate) that appears as legitimate hardware under `/dev/input`.

The implementation writes **`uinput::event::Keyboard`** and **`uinput::event::Relative`** events to this virtual device, conforming to the standard Linux input-event protocol. This ensures compatibility with both Xorg and Wayland compositors. Scroll deltas are transmitted as relative wheel events (`Relative::Wheel`). The module exports **`action_device_path`**, allowing users to configure the specific device file path for the synthetic input device.

## Windows: SendInput API Implementation

Windows event generation lives in **[`crates/openlogi-inject/src/inject/windows.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-inject/src/inject/windows.rs)**, utilizing the native **`SendInput`** API through the `winapi` crate.

For keyboard synthesis, the code constructs **`INPUT`** structures of type `INPUT_KEYBOARD` populated with virtual-key codes and modifier flags. Mouse events use `INPUT_MOUSE` structures with button flags such as **`MOUSEEVENTF_LEFTDOWN`**, **`MOUSEEVENTF_LEFTUP`**, and coordinate data. Scroll events are emitted by setting **`MOUSEEVENTF_WHEEL`** with the appropriate delta value.

## Architecture Flow: From Action to OS Event

The synthesis pipeline follows a strict dispatch pattern defined in **[`crates/openlogi-core/src/binding.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/binding.rs)**:

1. **Action Definition** – User remappings or scripts instantiate **`openlogi_core::binding::Action`** variants (shortcuts, mouse buttons, scroll deltas).
2. **Execution Dispatch** – The **`execute`** method routes the action to `openlogi_inject::execute`.
3. **Platform Selection** – Inside the injector, a **`#[cfg(target_os = "...")]`** match selects the appropriate platform module (`macos`, `linux`, or `windows`).
4. **Event Posting** – The selected module builds native event structures and posts them to the OS. Events may be tagged with **`SYNTHETIC_EVENT_USER_DATA`** to allow OpenLogi’s own event tap to identify and filter out recursive inputs.

## Practical Code Examples

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

// Simulate Cmd+C (copy) on macOS or Ctrl+C on Linux/Windows
let action = Action::from(Shortcut::Copy);
execute(&action);

```

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

// Simulate a middle-click (button 2) across all platforms
let action = Action::from(MouseButton::Middle);
execute(&action);

```

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

// Simulate smooth scrolling of 3 vertical lines
let action = Action::Scroll { dx: 0.0, dy: 3.0 };
execute(&action);

```

## Summary

- **OpenLogi** isolates platform-specific input generation in the `openlogi-inject` crate, enabling consistent cross-platform behavior.
- **macOS** utilizes the Core Graphics framework (`core_graphics` crate) with `CGEvent` structures, supporting keyboard, mouse, and quantized scroll events.
- **Linux** creates virtual input devices via the **uinput** subsystem, writing standard kernel input events compatible with major display servers.
- **Windows** calls the native **SendInput** API to synthesize `INPUT_KEYBOARD` and `INPUT_MOUSE` structures directly into the message queue.
- The architecture supports **synthetic event tagging** (`SYNTHETIC_EVENT_USER_DATA`) to prevent input loops when OpenLogi is monitoring its own output.

## Frequently Asked Questions

### How does OpenLogi prevent infinite loops when synthesizing its own input events?

OpenLogi tags synthetic events with **`SYNTHETIC_EVENT_USER_DATA`** during the posting phase. When the system’s event tap encounters an input bearing this metadata, it recognizes the event as internally generated and selectively ignores it, preventing recursive processing cycles.

### Can OpenLogi synthesize macOS-specific system actions like Mission Control?

Yes. While standard keys and mouse events use public **Core Graphics** APIs, native macOS workspace actions such as **`mission_control()`**, **`show_desktop()`**, and **`sleep_system()`** are implemented in [`crates/openlogi-inject/src/inject/macos.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-inject/src/inject/macos.rs) by invoking private SPIs or external utilities like AppleScript and `pmset`, as no public CGEvent types exist for these functions.

### What permissions are required for Linux input synthesis?

The **uinput**-based implementation requires write access to `/dev/uinput` or the specific device path configured via **`action_device_path`**. Typically, this necessitates either running the application with elevated privileges or adding the user to the `input` group, depending on the distribution’s udev rules.

### Why does OpenLogi use SendInput rather than SendMessage on Windows?

**SendInput** inserts events directly into the system’s raw input stream at a low level, ensuring that synthetic inputs are indistinguishable from physical hardware to most applications and games. In contrast, **`SendMessage`** targets specific window handles and operates at the message queue level, which many modern applications ignore or handle differently than physical input.