# How to Control DPI Settings for Logitech Mice Using OpenLogi

> Master your Logitech mouse DPI with OpenLogi. This guide shows how to control DPI settings using the HID++ protocol and a unified Rust API. Customize your precision.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: how-to-guide
- Published: 2026-08-21

---

**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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`): Calls `AdjustableDpiFeature::set_sensor_dpi` directly.
- For extended features (`0x2202`): Reads current parameters, preserves lift-off distance and Y-axis DPI values, then writes a complete `SetDpiParameters` struct.

The implementation in [`openlogi-hid/src/write/dpi.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-cli/src/cmd/diag/dpi.rs), this diagnostic tool performs the following operations:

1. **Device Selection**: Filters connected devices by feature IDs (`0x2201` or `0x2202`) using `select_device`.
2. **Capability Display**: Shows current DPI and supported values via `DpiSummaryDisplay`.
3. **Target Determination**: Accepts `--target` arguments or automatically selects a nearby test value differing from the current setting.
4. **Verification**: Writes the target DPI, reads it back, and validates the operation (handling "snapped" values where devices round to nearest supported DPI).
5. **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.

```bash

# 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:

```rust
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:

```rust
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 unified `DpiFeature` abstraction in [`openlogi-hid/src/write/dpi.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-hid/src/write/dpi.rs).
- Use **`openlogi_hid::get_dpi`** to read current values and **`openlogi_hid::set_dpi`** to write new settings, with automatic handling of protocol differences.
- The **`openlogi diag dpi`** CLI command provides a complete diagnostic workflow including verification and automatic cleanup.
- **`DpiCapabilities`** exposes supported DPI values through `values()` and `contains()` 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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/openlogi-cli/src/cmd/diag/dpi.rs) demonstrates this verification pattern including tolerance for hardware rounding behaviors.