How to Change DPI Settings for Logitech Devices with OpenLogi: A Complete Guide
OpenLogi enables DPI control through the HID++ protocol stack, exposing both a CLI diagnostic tool and Rust API that support Adjustable DPI (0x2201) and Extended Adjustable DPI (0x2202) features found in modern Logitech mice.
OpenLogi is an open-source framework for managing Logitech peripherals outside the official ecosystem. When you need to change DPI settings for Logitech devices with OpenLogi, the project provides a unified abstraction through the openlogi-hid crate, handling protocol negotiation and device capability detection automatically.
Understanding the HID++ DPI Features
Logitech mice implement DPI control through two distinct HID++ feature IDs that OpenLogi detects and handles transparently.
Adjustable DPI (0x2201)
The Adjustable DPI feature (0x2201) represents single-sensor DPI as a flat list of discrete values. This is the legacy implementation found in older Logitech gaming mice and productivity devices. According to the OpenLogi source code, this feature provides basic read/write access to sensor DPI without independent axis control.
Extended Adjustable DPI (0x2202)
Newer devices expose Extended Adjustable DPI (0x2202), which supports independent X/Y DPI configuration, lift-off distance adjustment, and stepped ranges. In crates/openlogi-hid/src/write/dpi.rs, the code handles this feature by reading current parameters and writing full SetDpiParameters structs to preserve device-specific settings like lift-off distance when modifying DPI values.
Feature Detection Logic
The internal DpiFeature enum probes for 0x2201 first, falling back to 0x2202 if necessary. As implemented in openlogi-hid/src/write/dpi.rs (lines 45-60), this ensures newer mice exposing only the extended feature remain fully operable while maintaining compatibility with legacy hardware.
Core API for DPI Control
The openlogi-hid crate exposes three primary functions for DPI management, all operating on a DeviceRoute obtained from the agent.
Reading Current DPI Values
openlogi_hid::get_dpi(&route) returns the current DPI for sensor 0. This function wraps the HID++ get_sensor_dpi and get_sensor_dpi_parameters calls, as seen in openlogi-hid/src/write/dpi.rs (lines 86-92). Both the diagnostic CLI and GUI components use this to display real-time DPI values.
use openlogi_hid;
let current_dpi = openlogi_hid::get_dpi(&route).await?;
println!("Current DPI: {}", current_dpi);
Writing New DPI Settings
openlogi_hid::set_dpi(&route, target) configures a new DPI value. Internally, this delegates to DpiFeature::set_dpi, which either calls AdjustableDpiFeature::set_sensor_dpi for the simple feature or, for extended features, reads current parameters before writing a full parameter struct (lines 111-124 in openlogi-hid/src/write/dpi.rs). This approach preserves Y-axis DPI and lift-off distance settings when changing values.
use openlogi_hid;
// Set DPI to 800 (must be a supported value)
openlogi_hid::set_dpi(&route, 800.into()).await?;
Querying Device Capabilities
openlogi_hid::DpiCapabilities describes supported DPI values through methods like values(), contains(), and adjacent_test_target(). The function expand_dpi_ranges (lines 58-84) parses raw HID++ responses from extended feature devices to populate these capabilities, allowing applications to validate user input against hardware limits before attempting writes.
use openlogi_hid::DpiCapabilities;
let info = openlogi_hid::get_dpi_info(&route).await?;
let caps = info.capabilities;
// Check if 1600 DPI is supported
if caps.contains(1600) {
println!("1600 DPI is available");
}
Using the CLI Diagnostic Tool
The openlogi diag dpi command provides immediate access to DPI functions without writing code. Located in crates/openlogi-cli/src/cmd/diag/dpi.rs, this diagnostic tool performs a complete read-write-verify cycle.
Basic DPI Inspection
Running the command without arguments displays the current DPI and all supported values:
openlogi diag dpi
This executes the DpiSummaryDisplay logic (lines 31-37) and filters for devices exposing either DPI feature ID (lines 26-30).
Setting Specific DPI Values
Use the --target flag to specify a desired DPI. The CLI determines the target either from this flag or by selecting a nearby supported value that differs from the current setting (lines 38-52):
# Set DPI to 800
openlogi diag dpi --target 800
# Target a specific device by name
openlogi diag dpi --device "MX Master 3" --target 1200
Verification and Restoration
The diagnostic command writes the target DPI, reads it back, and verifies the operation succeeded (lines 61-85). It handles "snapped" values where the device maps requests to the nearest supported DPI. Finally, it restores the original DPI to leave the device in its initial state (lines 89-94), making the tool safe for testing.
Programmatic DPI Control in Rust
For integration into custom applications, implement the following pattern using the openlogi-hid crate:
use openlogi_hid::{self, DpiCapabilities};
use openlogi_ipc::DeviceRoute;
use anyhow;
async fn change_dpi(route: &DeviceRoute, desired: u16) -> anyhow::Result<()> {
// 1️⃣ Query capabilities
let caps = openlogi_hid::get_dpi_info(route).await?.capabilities;
// Validate 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 the change
let after = openlogi_hid::get_dpi(route).await?;
println!("DPI now set to {after}");
Ok(())
}
To enumerate available options for a UI slider:
let info = openlogi_hid::get_dpi_info(&route).await?;
let values: Vec<u16> = info.capabilities.values();
println!("Supported DPI values: {:?}", values);
Architecture and Data Flow
OpenLogi's DPI control follows a layered architecture spanning multiple crates:
- UI/CLI Layer:
openlogi-cli(command line) andopenlogi-desktop(GUI) receive user input - IPC Layer:
openlogi-ipcroutes requests to the system agent - Agent Layer:
openlogi-agent(seesrc/tray.rs) processes requests and manages device connections - HID++ Layer:
openlogi-hid(seesrc/write/dpi.rs) translates API calls into HID++ protocol messages
The pure data types (Dpi, DpiCapabilities, DpiInfo) defined in crates/openlogi-core/src/hid/dpi.rs ensure type safety across all layers, while the settings UI in openlogi-desktop/src/windows/settings/general.rs demonstrates real-world integration of the DPI slider using these same APIs.
Summary
- OpenLogi supports both Adjustable DPI (0x2201) and Extended Adjustable DPI (0x2202) HID++ features through automatic detection
- Use
openlogi_hid::get_dpi()andopenlogi_hid::set_dpi()for programmatic control, oropenlogi diag dpifor CLI management - The
DpiCapabilitiesstruct validates supported values before writing, preventing invalid configuration attempts - The CLI diagnostic tool automatically verifies writes and restores original values, making it safe for testing
- Source implementations in
crates/openlogi-hid/src/write/dpi.rshandle protocol details like preserving lift-off distance and Y-axis settings during updates
Frequently Asked Questions
What HID++ features does OpenLogi support for DPI control?
OpenLogi implements two features: Adjustable DPI (0x2201) for basic single-sensor devices, and Extended Adjustable DPI (0x2202) for advanced mice supporting independent X/Y axes and lift-off distance. The DpiFeature enum in openlogi-hid/src/write/dpi.rs automatically probes for 0x2201 first, then falls back to 0x2202 if necessary.
How do I verify my DPI change was applied successfully?
The openlogi diag dpi command performs automatic verification by reading the DPI value immediately after writing and comparing it to the target. For programmatic use, call openlogi_hid::get_dpi(&route).await? after set_dpi() to confirm the device reports the expected value, handling cases where the mouse "snaps" to the nearest supported DPI.
Can I set different DPI values for X and Y axes?
Yes, but only on devices exposing the Extended Adjustable DPI (0x2202) feature. The basic set_dpi() API sets both axes to the same value, but the underlying implementation in DpiFeature::set_dpi preserves existing Y-axis values when modifying X-axis DPI for extended feature devices. For explicit independent control, you would need to construct a full SetDpiParameters struct with distinct X and Y values.
What happens if I request an unsupported DPI value?
The device will "snap" to the nearest supported value. OpenLogi handles this gracefully: the DpiCapabilities API provides values() and contains() methods to check support before writing, while the CLI tool reports the actual value post-write so you can see what the device selected. Always validate against DpiCapabilities first, or use adjacent_test_target() to find valid nearby values.
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 →