How Native Scroll Inversion Works on Supported Devices in OpenLogi
OpenLogi implements native scroll inversion by detecting the HID++ 0x2121 HiResWheel feature capability, persisting the user preference in per-device configuration, and writing the inversion flag directly to the firmware via the HiResWheel set_wheel_mode command.
OpenLogi is an open-source Rust implementation of Logitech device drivers that supports hardware-level scroll direction customization. The native scroll inversion feature allows compatible mice to reverse scroll direction in firmware rather than software, eliminating input lag and ensuring consistent behavior across operating systems. This deep integration requires precise coordination between the HID++ protocol layer, configuration management, and the desktop UI.
Capability Detection via HiResWheel Feature
Native scroll inversion depends on the HID++ HiResWheel feature (feature index 0x2121). When OpenLogi probes a connected device, the HiResWheelFeature struct queries the firmware capabilities to determine if hardware-level inversion is available.
Reading the has_invert Flag
The capability detection occurs in crates/openlogi-hidpp/src/feature/hires_wheel.rs within the get_wheel_capabilities function. This function returns a capability bitmask where the has_invert flag indicates whether the device supports native scroll direction toggling.
// From crates/openlogi-hidpp/src/feature/hires_wheel.rs (lines 51-58)
let caps = feature.get_wheel_capabilities().await?;
if caps.has_invert {
// Device supports native scroll inversion
}
If has_invert is false, the UI hides the toggle to prevent user confusion, as the firmware cannot honor inversion requests.
Configuration and Persistence
OpenLogi stores scroll preferences in a layered configuration system that supports both device-level defaults and per-link overrides for different connection types (USB, Bolt receiver, Unifying receiver).
Device-Level Storage
The primary configuration flag lives in crates/openlogi-core/src/config/device.rs as the invert_scroll field on DeviceConfig. This boolean represents the user's preference for the specific device model.
// From crates/openlogi-core/src/config/device.rs (lines 49-56)
pub struct DeviceConfig {
pub invert_scroll: bool,
// ... other fields
}
Effective Value Resolution
At runtime, OpenLogi resolves the final configuration by checking for per-route overrides first, then falling back to device-level settings. The method DeviceConfig::effective_invert_scroll handles this resolution logic in crates/openlogi-core/src/config/device.rs (lines 85-92).
// Retrieve effective setting for a specific connection route
let should_invert = config.effective_invert_scroll(&device_route_key);
This design allows users to invert scrolling on a USB connection while maintaining standard direction on a wireless receiver, or vice versa.
UI Interaction and State Management
The desktop application provides a native toggle control that bridges user interaction with the configuration backend.
Desktop Toggle Implementation
In crates/openlogi-desktop/src/app/detail.rs (lines 430-438), the UI constructs a toggle widget identified as "invert-scroll-toggle". When the user interacts with this control, it triggers the state commit flow rather than modifying configuration directly.
// From crates/openlogi-desktop/src/app/detail.rs
Toggle::new("invert-scroll-toggle")
.on_toggle(move |state| {
state.commit_invert_scroll(!current_value);
});
Committing Changes
The commit_invert_scroll method in the desktop state handler performs two critical operations: persisting the new value to the configuration store and dispatching a command to the HID layer to update the physical device state immediately.
Low-Level Device Communication
The actual hardware update travels through three architectural layers: the desktop state handler, the host HID abstraction, and finally the device-specific HID++ implementation.
Writing the Wheel Mode
The host layer exposes openlogi_hid::set_scroll_inversion, defined in crates/openlogi-hid/src/host.rs (lines 108-111). This function accepts a route identifier and the desired inversion boolean, forwarding the request to the appropriate device writer.
// From crates/openlogi-hid/src/host.rs
pub async fn set_scroll_inversion(
route: &DeviceRoute,
inverted: bool
) -> Result<(), Error> {
// Forwards to device-specific implementation
}
Command Structure and Bit Flags
The final command construction occurs in crates/openlogi-hidpp/src/feature/hires_wheel.rs (lines 84-95) within HiResWheelFeature::set_wheel_mode. This function sets bit 2 of the mode byte when the inverted parameter is true, following the HID++ specification for wheel mode configuration.
// From crates/openlogi-hidpp/src/feature/hires_wheel.rs
let mode = WheelMode {
// Bit 2 controls inversion
invert: inverted,
..current_mode
};
self.set_wheel_mode(mode).await?;
The device responds with a WheelMode structure reflecting the actual firmware state, allowing OpenLogi to confirm whether the inversion was successfully applied or rejected by the hardware.
Implementation Example
The following Rust patterns demonstrate the complete flow from configuration query to hardware application:
// 1. Query the effective inversion setting for a specific connection
let invert = config.effective_invert_scroll(&device_route_key);
// 2. Toggle the setting from UI context and persist
state.commit_invert_scroll(!invert);
// 3. Write the configuration directly to the HID device
openlogi_hid::set_scroll_inversion(&route, true).await?;
Summary
- Capability Detection: OpenLogi queries the HiResWheel feature's
has_invertflag during device enumeration to determine hardware support. - Layered Configuration: The
invert_scrollpreference supports both global device defaults and per-connection overrides viaeffective_invert_scroll. - UI Integration: The desktop toggle in
detail.rscallscommit_invert_scroll()to atomically persist and apply changes. - Hardware Protocol: Inversion is applied by setting bit 2 of the wheel mode byte through
HiResWheelFeature::set_wheel_modein the HID++ layer.
Frequently Asked Questions
Which Logitech devices support native scroll inversion in OpenLogi?
Devices that expose the HID++ 0x2121 HiResWheel feature with the has_invert capability flag support native inversion. This typically includes modern MX Master series mice and other high-resolution wheel devices. OpenLogi automatically detects this capability during the initial get_wheel_capabilities call and hides the toggle for unsupported hardware.
How does OpenLogi handle scroll inversion when switching between USB and wireless receivers?
The configuration system uses per-route resolution through DeviceConfig::effective_invert_scroll. If you enable inversion on the USB route but not on the wireless receiver route, OpenLogi maintains separate states for each connection type, querying the effective value based on the active device_route_key whenever the connection changes.
Can I use native scroll inversion alongside high-resolution scrolling?
Yes. The WheelMode structure in hires_wheel.rs combines multiple flags into a single byte, where bit 2 controls inversion independently from high-resolution (HiRes) mode settings. When calling set_wheel_mode, OpenLogi preserves the existing resolution and ratchet settings while only modifying the inversion bit.
What happens if the device rejects the inversion command?
When the HID++ layer sends set_wheel_mode, the firmware returns a WheelMode response reflecting the actual hardware state. If the device ignores or rejects the inversion request (for example, due to firmware restrictions), OpenLogi captures this response and updates the UI to reflect the true state rather than the requested state, ensuring the user interface remains synchronized with hardware behavior.
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 →