How to Toggle SmartShift Wheel Mode in OpenLogi: A Complete Technical Guide

OpenLogi toggles SmartShift wheel mode between free-spin and ratchet by reading the current HID++ feature 0x2110 status, flipping the mode enum, and writing the new state back to the device with automatic retry logic.

OpenLogi is an open-source Rust library for managing Logitech HID++ devices programmatically. Whether you are building a GUI application or automating mouse settings, understanding how to toggle SmartShift wheel mode allows you to switch between free-spin scrolling and tactile ratchet mode on compatible Logitech hardware. The implementation follows a layered architecture that separates protocol definitions from device communication.

Architecture Overview

The OpenLogi codebase organizes SmartShift functionality into four distinct layers, each handling a specific aspect of wheel mode toggling:

Core SmartShift Definitions

The foundational types for SmartShift wheel mode toggling reside in crates/openlogi-core/src/hid/smartshift.rs. This module defines the SmartShiftMode enum with variants Free (free-spin) and Ratchet (tactile clicking), along with the SmartShiftStatus struct that captures the current device state.

All higher-level operations reference these core types, ensuring type safety across the asynchronous communication boundary.

HID++ Protocol Implementation

The low-level device communication implements Logitech's HID++ feature 0x2110 (SmartShift) and its enhanced variant 0x2111. The source file crates/openlogi-hidpp/src/feature/smartshift.rs contains the SmartShiftFeature struct that handles:

  • read_status() – Queries the current wheel mode from the device
  • Write commands – Transmits mode changes via the WriteSmartShift HID++ command

This layer manages the binary protocol encoding and checksum validation required for reliable device communication.

Device-Level Write Logic

The high-level toggle logic lives in crates/openlogi-device/src/write/smartshift.rs, specifically between lines 301-313. The toggle_smartshift function implements a robust state machine that:

  1. Calls SmartShiftFeature::read_status to retrieve the current SmartShiftMode
  2. Computes the opposite variant (Free ↔ Ratchet)
  3. Executes the WriteSmartShift command with the new mode
  4. Returns the updated SmartShiftMode to the caller

To handle transient race conditions where the device briefly rejects writes, the implementation includes toggle_smartshift_on_channel with a short pause and retry block. This ensures reliable toggling even when the device is processing other HID reports.

Host API Integration

The public API surface in crates/openlogi-hid/src/host.rs (lines 69-73) exposes toggle_smartshift as an async function that selects the appropriate native backend (async-hid on the host) and forwards calls to the device layer. This façade pattern allows the same code to run on desktop applications, background agents, or embedded environments without modification.

CLI Command Interface

For manual testing and scripting, crates/openlogi-cli/src/cmd/diag/smartshift.rs implements the openlogi diag smartshift subcommand. This diagnostic tool provides:

  • --index flag to target specific devices by inventory position
  • --read flag to query current mode without changing state
  • Automatic mode toggling when invoked without flags

How the Toggle Mechanism Works

The complete SmartShift wheel mode toggling sequence operates through three distinct phases:

  1. Read current status – The system calls get_smartshift_status, which returns a SmartShiftStatus struct containing the active SmartShiftMode enum variant.

  2. Flip the mode – The toggle_smartshift function determines the opposite variant (switching Free to Ratchet or vice versa) and transmits the change via the HID++ write command.

  3. Retry logic – If the device reports a temporary failure, the implementation pauses briefly and retries the write operation through toggle_smartshift_on_channel, handling the race condition where the device buffer is temporarily busy.

All operations are fully asynchronous (async fn), enabling non-blocking use from GUI event loops, background services, or command-line interfaces.

Practical Implementation Examples

Rust API Usage

To toggle SmartShift programmatically from a Rust application:

use openlogi_hid::{backend, toggle_smartshift};
use openlogi_device::DeviceRoute;

// Toggle SmartShift mode for a specific device route
async fn flip_smartshift(route: &DeviceRoute) -> Result<(), openlogi_core::hid::WriteError> {
    let new_mode = toggle_smartshift(route).await?;
    println!("SmartShift is now {:?}", new_mode);
    Ok(())
}

// Usage within an async runtime:
// flip_smartshift(&device_route).await?;

CLI Usage

To toggle SmartShift from the terminal:


# List available devices to find the target index

openlogi list

# Toggle SmartShift for device at index 0

openlogi diag smartshift --index 0

# Read current mode without toggling

openlogi diag smartshift --index 0 --read

The CLI prints the new mode name (Free or Ratchet) after successful execution.

Summary

Frequently Asked Questions

What HID++ feature ID controls SmartShift in OpenLogi?

OpenLogi uses HID++ feature 0x2110 for standard SmartShift control and 0x2111 for the enhanced variant, as implemented in crates/openlogi-hidpp/src/feature/smartshift.rs. These feature IDs correspond to Logitech's official protocol specifications for wheel mode switching.

How does OpenLogi handle write failures when toggling SmartShift?

The toggle_smartshift_on_channel function in crates/openlogi-device/src/write/smartshift.rs implements a retry mechanism with a short pause to handle transient race conditions. If the device briefly rejects the write command due to busy buffers, the library automatically retries the operation before returning an error to the caller.

Can I use OpenLogi to toggle SmartShift from a command-line script?

Yes. The openlogi diag smartshift --index <n> command in crates/openlogi-cli/src/cmd/diag/smartshift.rs allows scripted toggling of the wheel mode. You can query the current state with the --read flag or toggle directly by omitting it, making it suitable for shell scripts and automation workflows.

Is the SmartShift toggle operation synchronous or asynchronous in OpenLogi?

All SmartShift operations are asynchronous (async fn). The public API in crates/openlogi-hid/src/host.rs exposes toggle_smartshift as an async function that integrates with the async-hid backend, ensuring non-blocking operation in GUI applications and background services.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →