How to Control DPI Settings for Logitech Mice Using OpenLogi
OpenLogi controls Logitech mouse DPI settings through the HID++ protocol by abstracting both the Adjustable DPI (0x2201) and Extended Adjustable DPI (0x2202) features behind a unified Rust API exposed in the openlogi-hid crate.
The OpenLogi project provides an open-source implementation for managing Logitech peripherals on Linux and Windows. To control DPI settings for Logitech mice using OpenLogi, developers interact with the openlogi-hid crate which implements the proprietary HID++ protocol stack, exposing simple read/write methods while handling the complexity of feature detection and protocol versioning internally.
Understanding OpenLogi's DPI Architecture
OpenLogi communicates with Logitech mice using the HID++ protocol, which exposes hardware capabilities through feature IDs. The DPI functionality is implemented in crates/openlogi-hid/src/write/dpi.rs, where the library abstracts protocol differences between device generations.
HID++ Protocol Features (0x2201 and 0x2202)
Logitech mice expose DPI control through two distinct HID++ features:
- Adjustable DPI (
0x2201): Supports single-sensor DPI represented as a flat list of discrete values. Found on older or simpler devices. - Extended Adjustable DPI (
0x2202): Supports independent X/Y DPI configuration, lift-off distance settings, and stepped ranges. Found on newer gaming mice.
According to the OpenLogi source code, the implementation probes for feature 0x2201 first and falls back to 0x2202 if necessary, ensuring compatibility across the entire Logitech product range.
The DpiFeature Abstraction Layer
The internal DpiFeature enum defined in openlogi-hid/src/write/dpi.rs unifies both protocol variants behind a common interface. When a device is accessed, DpiFeature::open attempts to initialize the simple feature first, automatically falling back to the extended feature if the primary probe fails. This abstraction ensures that newer mice exposing only the extended feature remain fully operable without requiring client code changes.
Core DPI Operations
The openlogi-hid crate exposes three primary operations for DPI management: reading current values, writing new settings, and parsing device capabilities.
Reading Current DPI Values
To query the current DPI setting, use the openlogi_hid::get_dpi(&route) function. This method returns the current DPI for sensor 0 as a thin wrapper around the HID++ get_sensor_dpi and get_sensor_dpi_parameters calls. For detailed capability information, openlogi_hid::get_dpi_info(&route) returns a DpiInfo struct containing both the current value and supported ranges.
As implemented in openlogi-hid/src/write/dpi.rs (lines 86-92), this function is used by the diagnostic CLI and UI components to display real-time DPI values.
Writing New DPI Settings
To change the DPI, invoke openlogi_hid::set_dpi(&route, target) where target is the desired DPI value. Internally, this delegates to DpiFeature::set_dpi, which handles protocol-specific implementation details:
- For simple features (
0x2201): CallsAdjustableDpiFeature::set_sensor_dpidirectly. - For extended features (
0x2202): Reads current parameters, preserves lift-off distance and Y-axis DPI values, then writes a completeSetDpiParametersstruct.
The implementation in openlogi-hid/src/write/dpi.rs (lines 111-124) ensures that extended feature writes maintain existing secondary settings while updating only the requested parameters.
Parsing Device Capabilities
The DpiCapabilities struct describes valid DPI values for a specific device, exposing methods like values(), contains(), and adjacent_test_target(). For extended features, capabilities are produced by the expand_dpi_ranges function (lines 58-84 in openlogi-hid/src/write/dpi.rs), which parses raw HID++ responses into usable Rust collections.
CLI Interface for DPI Control
The openlogi-cli crate provides the openlogi diag dpi command for interactive DPI management. Located in crates/openlogi-cli/src/cmd/diag/dpi.rs, this diagnostic tool performs the following operations:
- Device Selection: Filters connected devices by feature IDs (
0x2201or0x2202) usingselect_device. - Capability Display: Shows current DPI and supported values via
DpiSummaryDisplay. - Target Determination: Accepts
--targetarguments or automatically selects a nearby test value differing from the current setting. - Verification: Writes the target DPI, reads it back, and validates the operation (handling "snapped" values where devices round to nearest supported DPI).
- Cleanup: Restores the original DPI to leave the device in its initial state.
The DpiArgs struct (lines 11-23) defines available options including --target for specific values and --device for targeting specific mouse models by name.
# Show current DPI and supported values for the first Logitech mouse found
openlogi diag dpi
# Set DPI to 800 (must be a supported value); the command will verify the write
openlogi diag dpi --target 800
# Force a specific device name (useful when multiple devices are paired)
openlogi diag dpi --device "MX Master 3" --target 1200
Implementing DPI Control in Rust
For integration into custom applications, import the openlogi-hid crate and use the asynchronous API:
use openlogi_hid::{self, DpiCapabilities};
use openlogi_ipc::DeviceRoute; // route obtained from the agent
async fn change_dpi(route: &DeviceRoute, desired: u16) -> anyhow::Result<()> {
// 1️⃣ Query capabilities
let caps = openlogi_hid::get_dpi_info(route).await?.capabilities;
// Ensure the desired DPI is supported
let dpi = caps
.values()
.into_iter()
.find(|&v| v == desired)
.ok_or_else(|| anyhow::anyhow!("Unsupported DPI {desired}"))?;
// 2️⃣ Write DPI
openlogi_hid::set_dpi(route, dpi.into()).await?;
// 3️⃣ Verify
let after = openlogi_hid::get_dpi(route).await?;
println!("DPI now set to {after}");
Ok(())
}
To enumerate supported values programmatically:
let info = openlogi_hid::get_dpi_info(&route).await?;
println!("Supported DPI values: {}",
openlogi_hid::DpiCapabilities::new(info.capabilities.values()).unwrap());
The desktop frontend (openlogi-desktop) and overlay (openlogi-overlay) utilize this same API, creating DPI sliders that call openlogi_hid::set_dpi and openlogi_hid::get_dpi to ensure consistent behavior across CLI and GUI interfaces.
Summary
- OpenLogi supports both Adjustable DPI (
0x2201) and Extended Adjustable DPI (0x2202) HID++ features through the unifiedDpiFeatureabstraction inopenlogi-hid/src/write/dpi.rs. - Use
openlogi_hid::get_dpito read current values andopenlogi_hid::set_dpito write new settings, with automatic handling of protocol differences. - The
openlogi diag dpiCLI command provides a complete diagnostic workflow including verification and automatic cleanup. DpiCapabilitiesexposes supported DPI values throughvalues()andcontains()methods, parsing extended range formats automatically.- The architecture follows a clear path: UI/CLI → IPC → Agent → HID++ driver → Logitech mouse, ensuring consistent behavior across all OpenLogi components.
Frequently Asked Questions
What HID++ features does OpenLogi support for DPI control?
OpenLogi implements support for Adjustable DPI (0x2201) and Extended Adjustable DPI (0x2202) as defined in the Logitech HID++ protocol specification. The 0x2201 feature provides basic single-sensor DPI lists, while 0x2202 adds independent X/Y axis control, lift-off distance configuration, and stepped range support. The library in openlogi-hid/src/write/dpi.rs abstracts both protocols behind a common interface.
How does OpenLogi handle devices with different DPI feature versions?
The DpiFeature::open method in openlogi-hid/src/write/dpi.rs probes for feature 0x2201 first, then automatically falls back to 0x2202 if the simple feature is unavailable. This ensures newer mice that expose only the extended feature work correctly without requiring application-level changes. The abstraction layer handles protocol-specific read and write implementations transparently.
Can I set separate X and Y axis DPI values using OpenLogi?
When using devices that expose the Extended Adjustable DPI (0x2202) feature, the DpiFeature implementation preserves existing X/Y values when setting new DPI. The set_dpi function reads current parameters via get_sensor_dpi_parameters, modifies only the requested axis (or combined value), and writes a complete SetDpiParameters struct. Direct independent axis control requires using the lower-level HID++ feature methods rather than the high-level set_dpi wrapper.
How do I verify that my DPI change was applied successfully?
The openlogi diag dpi command automatically verifies writes by reading the DPI value after setting it and comparing against the target, handling cases where the device "snaps" to the nearest supported value. When using the Rust API, call openlogi_hid::get_dpi after openlogi_hid::set_dpi to confirm the new value matches expectations. The diagnostic tool in openlogi-cli/src/cmd/diag/dpi.rs demonstrates this verification pattern including tolerance for hardware rounding behaviors.
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 →