How to Configure SmartShift Wheel Settings in OpenLogi: A Complete Technical Guide

OpenLogi configures SmartShift wheel settings through a four-layer architecture spanning core configuration structs in settings.rs, HID++ protocol mappings in smartshift.rs, IPC messages in the openlogi-ipc crate, and the desktop UI panel, with all values persisted to config.toml to survive device power cycles.

OpenLogi is an open-source driver replacement for Logitech devices that exposes the SmartShift ratchet mechanism through a Rust-based configuration stack. Configuring SmartShift wheel settings requires understanding how the codebase bridges high-level user preferences with low-level HID++ protocol commands. This guide walks through the specific file paths and APIs that control wheel mode, auto-disengage thresholds, and torque settings.

Core Configuration Structure

The foundation for SmartShift configuration lives in crates/openlogi-core/src/config/settings.rs. This file defines the SmartShift struct (lines 713-721) that persists user preferences between sessions.

The configuration includes:

  • WheelMode enum: Defines Free and Ratchet states for the wheel mechanism
  • Auto-disengage thresholds: Validated against SMARTSHIFT_MIN_AUTO_DISENGAGE and SMARTSHIFT_AUTO_DISENGAGE_DEFAULT constants
  • Structural validation: The struct enforces type safety for all wheel parameters via Rust's type system

HID++ Protocol Mapping

The translation between Rust structs and hardware bytes happens in crates/openlogi-core/src/hid/smartshift.rs. This module handles the wire format for Logitech's HID++ protocol.

Key components include:

  • SmartShiftMode enum (lines 33-39): Represents hardware values for ratchet states with a flipped() helper method to toggle between modes
  • SmartShiftThreshold: Wraps raw threshold values with from_rounded() for UI conversions and try_new() for validation
  • SmartShiftAutoDisengage: Enum distinguishing between threshold-based and permanent ratchet modes

Low-level device writes are implemented in crates/openlogi-device/src/write/smartshift.rs, which formats these structures into HID++ packets.

Agent-GUI Communication Layer

The openlogi-ipc crate defines the contract between the desktop GUI and background agent. The relevant RPC endpoints are:

  • set_smartshift (lines 465-472 in ipc.rs): Transmits SmartShiftStatus from UI to agent
  • read_smartshift: Queries current hardware state for UI initialization

These messages carry the same structures defined in the core crate, ensuring type safety across process boundaries.

Desktop UI Implementation

The user-facing controls reside in crates/openlogi-desktop/src/features/pointer/smartshift.rs. This panel renders three primary controls that map directly to the configuration structs.

Wheel Mode Selection

Segmented buttons toggle between SmartShiftMode::Free and SmartShiftMode::Ratchet, immediately updating the device state via AppState::update_smartshift.

Auto-Disengage Threshold

A slider control (handled in the threshold_sub subscription, lines 90-108) writes SmartShiftAutoDisengage::Threshold(value) when the user releases the slider. The handler clamps values using THRESHOLD_MIN and THRESHOLD_MAX constants before transmission.

Permanent Ratchet Toggle

When enabled, this writes SmartShiftAutoDisengage::Permanent to the hardware, bypassing threshold-based switching entirely.

Persisting SmartShift Settings

SmartShift settings survive power cycles through config.toml persistence. The AppState::commit_smartshift method serializes current values to disk, while the agent reapplies these settings on device reconnection.

This persistence is critical because Logitech devices store SmartShift configuration in volatile RAM. As noted in the source comments (lines 8-13 of the UI file), without OpenLogi's persistence layer, users would lose their wheel settings every time the mouse powers down.

Programmatic Configuration Examples

Configure SmartShift to Ratchet mode with a specific threshold via the core types:

use openlogi_core::hid::{
    SmartShiftAutoDisengage, SmartShiftMode, SmartShiftStatus, SmartShiftThreshold,
};

let new_status = SmartShiftStatus {
    mode: SmartShiftMode::Ratchet,
    auto_disengage: SmartShiftAutoDisengage::Threshold(
        SmartShiftThreshold::try_new(20).unwrap()
    ),
    tunable_torque: None,
};

// Transmit via IPC to agent
openlogi_ipc::set_smartshift(device_route, new_status).await?;

The UI implements threshold changes using subscription handlers that validate ranges:

let threshold_sub = cx.subscribe(&threshold, |panel, _slider, event, cx| {
    match event {
        SliderEvent::Release(value) => {
            let thresh = SmartShiftThreshold::from_rounded(value.start())
                .clamp(THRESHOLD_MIN, THRESHOLD_MAX);
            
            AppState::update_smartshift(
                cx,
                SmartShiftStatus {
                    mode: SmartShiftMode::Ratchet,
                    auto_disengage: SmartShiftAutoDisengage::Threshold(thresh),
                    ..status
                },
            );
        }
        _ => {}
    }
});

Summary

Frequently Asked Questions

Where does OpenLogi store SmartShift wheel settings?

OpenLogi stores SmartShift configuration in a local config.toml file rather than depending on the device's volatile memory. According to the source comments in crates/openlogi-desktop/src/features/pointer/smartshift.rs (lines 8-13), the hardware only keeps these settings in RAM, so OpenLogi's persistence layer ensures your ratchet preferences survive power cycles and reconnections.

What is the valid range for SmartShift auto-disengage thresholds?

The auto-disengage threshold range is defined by SMARTSHIFT_MIN_AUTO_DISENGAGE and SMARTSHIFT_AUTO_DISENGAGE_DEFAULT constants in settings.rs. When setting values programmatically, use SmartShiftThreshold::try_new() for validation or from_rounded() with clamping to ensure the value falls within hardware-supported limits.

How do I toggle between Free spin and Ratchet modes programmatically?

Use the SmartShiftMode enum defined at lines 33-39 in crates/openlogi-core/src/hid/smartshift.rs, which provides Free and Ratchet variants. The enum includes a flipped() helper method to toggle between states. Construct a SmartShiftStatus with your chosen mode and pass it to openlogi_ipc::set_smartshift() to update the device.

What happens when I adjust the threshold slider in the OpenLogi desktop app?

The UI subscribes to slider release events via threshold_sub (lines 90-108 in smartshift.rs). Upon release, it converts the raw value using SmartShiftThreshold::from_rounded(), clamps it to valid bounds, and calls AppState::update_smartshift() to send the new configuration through the IPC layer to the agent, which writes it to both the hardware and the persistent config file.

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 →