# How OpenLogi Implements Per-Device Scroll Inversion via HID++ 0x2121

> Discover how OpenLogi enables per-device scroll inversion using HID++ 0x2121. Learn about its method of detecting capabilities and issuing SetFeature commands for scroll wheel direction control.

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

---

**OpenLogi toggles scroll-wheel direction per device by detecting the HID++ 0x2121 capability during enumeration and issuing SetFeature commands through the HID layer to set the inversion flag in volatile device memory.**

OpenLogi, an open-source alternative to Logitech Options, enables users to invert scroll-wheel direction on individual Logitech mice through the HID++ protocol. This article examines how the Rust-based codebase discovers, stores, and persists the per-device scroll inversion setting using HID++ feature ID 0x2121.

## Capability Discovery in the Device Registry

When a Logitech device connects, the device-registry probes the HID++ capabilities reported by the hardware. In [`crates/openlogi-device/src/inventory/features.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-device/src/inventory/features.rs) at line 324, the code checks for the `Capabilities::scroll_inversion` bit corresponding to feature 0x2121. If the device reports this capability, the `DeviceCapabilities` struct records `scroll_inversion = true`, signaling that the hardware supports scroll-direction inversion.

The core abstraction `openlogi_core::device::Device` stores this flag as a boolean field `scroll_inversion: bool` at line 114 of [`crates/openlogi-core/src/device.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/device.rs). This centralizes device capability knowledge within the core domain model.

## Core State and UI Exposure

The desktop GUI reads the capability flag via `current_scroll_inversion_supported()` defined in [`crates/openlogi-desktop/src/state/scroll.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-desktop/src/state/scroll.rs) at line 25. This function queries the underlying `Device` state to determine whether the "Invert Scroll" toggle should be enabled in the user interface. If the device lacks the 0x2121 feature, the UI disables the toggle and displays the error message `pointer.scroll_inversion_unsupported`.

## Writing the Scroll Inversion Setting to Hardware

### HID API Layer

The public API entry point `openlogi_hid::set_scroll_inversion` in [`crates/openlogi-hid/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hid/src/lib.rs) (line 37) accepts a device route and boolean flag, forwarding the request to the device-write module. This abstraction allows the GUI and automation scripts to toggle inversion without directly handling HID++ protocol details.

### Device Write Implementation

Within the device layer, the write module constructs a HID++ `SetFeature` request targeting the 0x2121 feature. The function `set_scroll_inversion_on` sends this request through the HID++ channel to toggle the inversion flag on the target device. The implementation resides in the device-write subsystem (referenced in `crates/openlogi-device/src/write` modules), where the raw HID++ packet is assembled and transmitted.

### Agent Orchestration

The agent core routes GUI changes to the physical hardware via `openlogi_hid::set_scroll_inversion_on` at line 381 of [`crates/openlogi-agent-core/src/hardware.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent-core/src/hardware.rs). This ensures that user interactions in the desktop application propagate through the entire stack—from UI state to HID API to device-specific write commands—before reaching the mouse firmware.

## Persistence Across Reconnections

Because the inversion setting lives only in volatile device memory, OpenLogi reapplies the saved value on each reconnection. The orchestrator monitors device availability and rewrites the stored inversion flag when a device becomes active, ensuring that user preferences survive sleep/wake cycles or USB reconnection events.

## Implementation Example

Query device capabilities and toggle scroll inversion using the OpenLogi API:

```rust
// Query whether a device supports scroll inversion
let caps = device.capabilities();
if caps.scroll_inversion {
    println!("Device can invert scroll direction");
}

// Invert scrolling for a specific route
use openlogi_hid::set_scroll_inversion;
let route = /* DeviceRoute obtained from inventory */;
set_scroll_inversion(&route, true).await?;   // true → invert, false → normal

```

## Summary

- **Capability Detection:** The device registry probes HID++ feature 0x2121 during enumeration at `inventory/features.rs:324`, storing the result in `DeviceCapabilities`.
- **State Management:** The core device model holds the `scroll_inversion` flag at `core/device.rs:114`, exposed to the UI via `current_scroll_inversion_supported()`.
- **Hardware Control:** The HID API (`hid/src/lib.rs:37`) and agent core (`agent-core/src/hardware.rs:381`) coordinate to send `SetFeature` commands that toggle the inversion bit in device memory.
- **Persistence:** The orchestrator reapplies saved inversion settings on every device reconnection to maintain user preferences across sessions.

## Frequently Asked Questions

### What is HID++ feature 0x2121?

HID++ feature 0x2121 is the "Scroll Inversion" capability in Logitech's HID++ protocol. When a device reports this feature bit during capability discovery, it indicates that the firmware supports reversing scroll-wheel direction via software commands.

### Does scroll inversion persist after unplugging the mouse?

No, the inversion setting resides in volatile device memory and resets when the mouse loses power. OpenLogi handles this by detecting reconnections through the orchestrator and automatically reapplying the saved inversion flag to restore the user's preferred scroll direction.

### How does OpenLogi handle devices without scroll inversion support?

If the device registry does not detect the 0x2121 capability bit in [`inventory/features.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/inventory/features.rs), the core device model sets `scroll_inversion = false`. The desktop state ([`desktop/src/state/scroll.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/desktop/src/state/scroll.rs)) then disables the "Invert Scroll" toggle and displays an unsupported error message to prevent invalid configuration attempts.

### Can scroll inversion be toggled programmatically via the API?

Yes, developers can call `openlogi_hid::set_scroll_inversion` with a valid `DeviceRoute` and boolean value to programmatically control scroll direction. This function is the same entry point used by the GUI and is available to any Rust code linking against the `openlogi_hid` crate.