How SmartShift Wheel Mode Toggles and Sensitivity Controls Work in OpenLogi

OpenLogi implements SmartShift wheel mode toggles and sensitivity controls through a layered HID++ architecture that discovers divertable buttons, manages gesture sessions, and exposes user-facing configuration options in the desktop interface.

OpenLogi is an open-source Rust implementation for managing Logitech HID++ devices. The repository provides complete control over SmartShift functionality, allowing programmatic toggling between free-spin and ratchet modes while adjusting auto-disengage sensitivity through both low-level APIs and desktop UI components.

The HID++ Architecture for SmartShift Management

OpenLogi organizes SmartShift control across four distinct layers according to the source code in AprilNEA/OpenLogi.

Device-Level HID++ API

The lowest layer handles raw reads and writes to mouse firmware. In crates/openlogi-hid/src/lib.rs and crates/openlogi-hid/src/host.rs, functions like set_smartshift_sensitivity and toggle_smartshift provide direct hardware communication.

Reprogrammable Control Discovery

The middle layer discovers and diverts SmartShift-related buttons. The ReprogControlsV4 feature (ID 0x1b04) enumerates controls defined in crates/openlogi-device/src/reprog_controls.rs, specifically the DPI_MODE_SHIFT_CIDS constant containing the three possible control IDs for SmartShift buttons (0x00c4, 0x00ed, 0x00fd).

Gesture Session Logic

During active sessions managed in crates/openlogi-device/src/session/gesture.rs, the system arms divertable controls and maps them to internal button identifiers like ButtonId::DpiToggle.

Desktop UI Layer

The presentation layer in crates/openlogi-desktop/src/windows/settings/general.rs and crates/openlogi-desktop/src/features/pointer/smartshift.rs exposes sliders for thumbwheel and vertical scroll sensitivity, binding user interactions to the underlying HID++ calls.

How SmartShift Wheel Mode Toggling Works

The toggle mechanism operates through a three-phase process involving hardware discovery, control diversion, and event translation.

Control Discovery via ReprogControlsV4

The system first enumerates available controls using the ReprogControlsV4 feature. The constant DPI_MODE_SHIFT_CIDS in reprog_controls.rs identifies SmartShift-capable buttons:

/// Control IDs of the "DPI / ModeShift" button family.
pub const DPI_MODE_SHIFT_CIDS: [u16; 3] = [0x00c4, 0x00ed, 0x00fd];

Arming and Diverting Buttons

During gesture session initialization, the arm_controls_into function in gesture.rs iterates through these CIDs. If a control is present and divertable, the system calls arm_reprog_control with raw_xy = false, preventing raw wheel data transmission during button holds:

for &cid in &reprog_controls::DPI_MODE_SHIFT_CIDS {
    if controls.iter().any(|c| c.cid == cid && c.is_divertable()) {
        arm_reprog_control(&rc, cid, false, &mut armed.reporting).await?;
        armed.dpi_cids.push(cid);
    }
}

Event Mapping to DpiToggle

Once diverted, the SmartShift button events translate to ButtonId::DpiToggle via the gesture_source_button mapping function. This allows the desktop UI to receive standardized events regardless of specific hardware control IDs.

Managing SmartShift Sensitivity Controls

Sensitivity control follows a similar layered approach, bridging user interface elements with hardware registers.

Low-Level HID++ Host API

The set_smartshift_sensitivity function in crates/openlogi-hid/src/host.rs accepts a DeviceRoute and SmartShiftAutoDisengage value, forwarding requests to the native backend driver:

/// Set the SmartShift auto‑disengage sensitivity of the device `route` reaches.
pub async fn set_smartshift_sensitivity(
    route: DeviceRoute,
    value: SmartShiftAutoDisengage,
) -> Result<(), WriteError> {
    device::set_smartshift_sensitivity(&*native_backend(), route, value).await
}

Device Write Wrappers

Higher-level management resides in crates/openlogi-device/src/write/smartshift.rs. The set_sensitivity method preserves the current SmartShift mode while updating only the auto-disengage threshold.

Desktop UI Integration

The settings panel in general.rs binds sliders to thumbwheel_sensitivity_field and vertical_scroll_sensitivity_field. When users adjust values, the UI invokes state.set_thumbwheel_sensitivity(), which ultimately calls set_smartshift_sensitivity for thumbwheel adjustments or set_scroll_resolution for vertical scrolling.

Implementation Examples

Toggling SmartShift Mode Programmatically

To toggle between free-spin and ratchet modes directly:

use openlogi_hid::set_smartshift;      // low‑level HID++ call
use openlogi_hid::toggle_smartshift;   // convenience wrapper

// `route` identifies the mouse (e.g. from `device::select_route()`).
async fn toggle_smart_shift(route: DeviceRoute) -> Result<(), WriteError> {
    // Directly toggle the mode (free‑spin ↔ ratchet)
    toggle_smartshift(route).await
}

Adjusting Auto-Disengage Sensitivity

Set the threshold that determines when the wheel transitions from free-spin to ratchet mode:

use openlogi_hid::set_smartshift_sensitivity;
use openlogi_hid::SmartShiftAutoDisengage;

async fn set_smart_shift_sensitivity(route: DeviceRoute, level: u8) -> Result<(), WriteError> {
    // `level` is 0‑255; the driver maps it to a usable auto‑disengage value.
    let sens = SmartShiftAutoDisengage::from(level);
    set_smartshift_sensitivity(route, sens).await
}

Capturing SmartShift Button Events

To intercept SmartShift button presses during gesture sessions:

use openlogi_device::reprog_controls::DPI_MODE_SHIFT_CIDS;

// Inside a gesture‑session setup:
let mut armed = ArmedControls::default();
for &cid in &DPI_MODE_SHIFT_CIDS {
    if controls.iter().any(|c| c.cid == cid && c.is_divertable()) {
        // Divert the button so the session can see it as a plain press
        arm_reprog_control(&rc, cid, false, &mut armed.reporting).await?;
        armed.dpi_cids.push(cid);
    }
}

Building UI Sensitivity Sliders

For desktop UI implementations using GPUI:

fn thumbwheel_sensitivity_field(slider: &Entity<SliderState>, cx: &mut App) -> gpui::Div {
    sensitivity_field(
        "Thumbwheel Sensitivity",
        slider.clone(),
        cx,
        |value| {
            let sens = ThumbwheelSensitivity::from_rounded(value.start());
            state.set_thumbwheel_sensitivity(sens);
        },
    )
}

Summary

  • OpenLogi implements SmartShift wheel mode toggles and sensitivity controls through a four-layer HID++ architecture spanning device firmware, control discovery, gesture management, and desktop UI.
  • The system identifies SmartShift buttons via the DPI_MODE_SHIFT_CIDS constant (0x00c4, 0x00ed, 0x00fd) in reprog_controls.rs and diverts them during gesture sessions to prevent raw data interference.
  • Sensitivity adjustments flow from UI sliders through set_smartshift_sensitivity in the HID++ host API, preserving existing wheel modes while updating auto-disengage thresholds.
  • All functionality is accessible programmatically through async Rust APIs in openlogi-hid and openlogi-device crates, or graphically through the OpenLogi desktop settings panel.

Frequently Asked Questions

How does OpenLogi identify which button controls SmartShift on different mouse models?

OpenLogi uses the ReprogControlsV4 feature (ID 0x1b04) to enumerate all programmable controls on the device. It specifically checks against the DPI_MODE_SHIFT_CIDS array containing three possible control IDs (0x00c4, 0x00ed, 0x00fd) defined in crates/openlogi-device/src/reprog_controls.rs. This covers the SmartShift button family across various Logitech hardware implementations, ensuring compatibility without hardcoding specific device models.

What is the difference between toggle_smartshift and set_smartshift_sensitivity?

toggle_smartshift, exported from crates/openlogi-hid/src/lib.rs, switches the wheel between free-spin and ratchet modes immediately. In contrast, set_smartshift_sensitivity, defined in crates/openlogi-hid/src/host.rs, adjusts the auto-disengage threshold—the force level at which a free-spinning wheel automatically switches to ratchet mode. The former changes the operating mode directly, while the latter configures the sensitivity parameter for automatic mode transitions.

Why does OpenLogi divert SmartShift buttons during gesture sessions with raw_xy = false?

The arm_reprog_control call in crates/openlogi-device/src/session/gesture.rs sets raw_xy = false when diverting SmartShift buttons to prevent the wheel from transmitting raw motion data while the button is held. This ensures the button acts as a plain modifier or toggle rather than a continuous input source, allowing the gesture system to capture clean button press events without interference from wheel movement data during the diversion period.

Can SmartShift sensitivity be adjusted independently for thumb wheels and vertical scrolling?

Yes. The desktop UI in crates/openlogi-desktop/src/windows/settings/general.rs exposes separate sliders for thumbwheel_sensitivity_field and vertical_scroll_sensitivity_field. Thumbwheel sensitivity adjustments invoke set_smartshift_sensitivity specifically for the SmartShift mechanism, while vertical scroll sensitivity uses set_scroll_resolution. Both settings persist independently in the device firmware, allowing distinct sensitivity profiles for different wheel types.

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 →