How SmartShift Wheel Mode Toggling and Permanent-Ratchet Panels Work in OpenLogi

OpenLogi enables SmartShift wheel-mode toggling through temporary CID diversion on button press, while the permanent-ratchet Haptic panel uses continuous raw-XY streaming via permanent diversion of its dedicated control ID.

OpenLogi communicates with Logitech HID++ devices using the ReprogControlsV4 feature (ID 0x1b04) to remap physical controls dynamically. According to the AprilNEA/OpenLogi source code, the driver handles two distinct interaction patterns: a temporary diversion for SmartShift mode switching and a permanent diversion for Haptic Sense panel input. Both mechanisms rely on control ID (CID) reporting changes but differ fundamentally in lifecycle and data consumption.

Understanding the HID++ ReprogControlsV4 Foundation

The ReprogControlsV4 feature exposes a table of reprogrammable controls that OpenLogi can divert from their default firmware behavior. Each physical button or touch surface maps to a specific CID (Control ID). In crates/openlogi-device/src/reprog_controls.rs, OpenLogi defines two critical CID families:

  • DPI_MODE_SHIFT_CIDS: Contains 0x00c4 (primary SmartShift button) with fallbacks 0x00ed and 0x00fd
  • HAPTIC_PANEL_CID: Maps to 0x00c3 or 0x00c5 for the thumb-rest touch panel

Diversion is controlled via set_cid_reporting, which instructs the device to send raw HID++ events instead of executing hardcoded actions.

SmartShift Wheel-Mode Toggling via Temporary Diversion

SmartShift functionality requires detecting wheel movement while a specific button is held. OpenLogi implements this through temporary diversion—the CID is only remapped during the button press.

CID Discovery and Identification

During device initialization, OpenLogi scans the control table using get_count and get_cid_info to locate a matching CID from DPI_MODE_SHIFT_CIDS. The source code in reprog_controls.rs defines this array to include the standard SmartShift button ID 0x00c4 along with legacy fallback identifiers.

Enabling Temporary Diversion

When the user presses the SmartShift button, OpenLogi invokes set_cid_reporting with diverted = true and raw_xy = true. This transmits a setCidReporting packet that temporarily reroutes the control and enables relative motion reporting:

let smart_shift_cid = openlogi_device::reprog_controls::DPI_MODE_SHIFT_CIDS[0];

// When SmartShift button is pressed
reprog
    .set_cid_reporting(smart_shift_cid, true, true) // divert + raw-XY
    .await?;

This state persists only while the button remains held.

Processing Raw-XY Events

While diverted, the device emits divertedButtonsEvent (function 0) and rawXYEvent (function 1). OpenLogi decodes these using decode_event in reprog_controls.rs, yielding a RawControlEvent containing wheel-delta values:

use openlogi_device::reprog_controls::{decode_event, RawControlEvent};

if let Some(RawControlEvent::RawXY { dx, dy }) = decode_event(packet)? {
    // Interpret dx/dy as wheel movement magnitude and direction
}

Mode Switch Logic

The driver interprets accumulated wheel-delta while the button is held as a request to toggle SmartShift mode (enabling ratchet-free scrolling or DPI preset changes). Upon button release, OpenLogi clears the diversion:

reprog
    .set_cid_reporting(smart_shift_cid, false, false)
    .await?;

This returns the control to firmware-defined behavior until the next press.

Permanent-Ratchet Haptic Panel Implementation

The Haptic Sense panel (thumb-rest touch area on MX Master 4 and newer) provides continuous input without requiring a held button. OpenLogi treats this as a permanent diversion scenario.

Continuous Raw-XY Streaming

Unlike SmartShift's temporary toggle, the Haptic panel is diverted permanently during driver initialization. OpenLogi calls set_cid_reporting_full with CidReportingChange::permanent_diversion to ensure the panel continuously streams raw-XY data:

let haptic_cid = openlogi_device::reprog_controls::HAPTIC_PANEL_CID;

reprog
    .set_cid_reporting_full(
        haptic_cid,
        hidpp_reprog::CidReportingChange::permanent_diversion(true, true),
    )
    .await?;

This enables smooth, ratchet-free scrolling gestures without button-state dependencies.

Handling First-Sample Jumps

The physical implementation of capacitive touch sensors causes the first raw-XY sample after contact to contain a large positional jump. As noted in reprog_controls.rs (lines 48-53), OpenLogi explicitly discards this initial sample before feeding data into the swipe accumulator to prevent erratic cursor movement.

Code Implementation Examples

Complete implementation requires coordinating the HID++ feature access with event processing:

Temporary SmartShift Control:

// crates/openlogi-device/src/reprog_controls.rs provides the high-level API
use openlogi_device::reprog_controls::{DPI_MODE_SHIFT_CIDS, RawControlEvent};

async fn handle_smart_shift(reprog: &ReprogControlsV4, packet: &[u8]) -> Result<()> {
    let cid = DPI_MODE_SHIFT_CIDS[0];
    
    // Press: enable diversion
    reprog.set_cid_reporting(cid, true, true).await?;
    
    // Event loop
    if let Some(event) = decode_event(packet)? {
        match event {
            RawControlEvent::RawXY { dx, dy } => {
                // Toggle logic based on wheel delta
                if dy.abs() > THRESHOLD {
                    toggle_smart_shift_mode();
                }
            }
            _ => {}
        }
    }
    
    // Release: disable diversion
    reprog.set_cid_reporting(cid, false, false).await?;
    Ok(())
}

Permanent Haptic Panel Initialization:

use openlogi_device::reprog_controls::HAPTIC_PANEL_CID;

async fn init_haptic_panel(reprog: &ReprogControlsV4) -> Result<()> {
    reprog
        .set_cid_reporting_full(
            HAPTIC_PANEL_CID,
            CidReportingChange::permanent_diversion(true, true),
        )
        .await
}

Key Source Files and Architecture

File Role
crates/openlogi-device/src/reprog_controls.rs Defines DPI_MODE_SHIFT_CIDS, HAPTIC_PANEL_CID, and high-level API (set_cid_reporting, decode_event)
crates/openlogi-hidpp/src/feature/reprog_controls/control_ids.rs Enumerates raw control IDs including SmartShift (0xC4)
crates/openlogi-hidpp/src/feature/reprog_controls/task_ids.rs Defines task IDs such as SmartShift (0x9D)
crates/openlogi-device/src/session/gesture.rs Implements gesture recognition consuming raw-XY streams from both control types

Summary

  • SmartShift uses temporary diversion of CID 0x00c4 (or fallbacks) only while the button is pressed, enabling wheel-delta detection for mode toggling.
  • The Haptic panel employs permanent diversion of CID 0x00c3/0x00c5 to provide continuous raw-XY streaming for ratchet-free scrolling.
  • Both mechanisms rely on ReprogControlsV4 feature ID 0x1b04 and the set_cid_reporting API, but differ in diversion duration and initialization timing.
  • OpenLogi filters first-sample jumps from the Haptic panel to ensure smooth gesture recognition.

Frequently Asked Questions

What CID identifies the SmartShift button in OpenLogi?

OpenLogi identifies the SmartShift button using CID 0x00c4, defined as the first element in DPI_MODE_SHIFT_CIDS within reprog_controls.rs. The implementation includes fallback IDs 0x00ed and 0x00fd for compatibility with different device firmware versions.

How does temporary diversion differ from permanent diversion?

Temporary diversion activates only while a physical button remains pressed, returning to firmware control immediately upon release via set_cid_reporting(cid, false, false). Permanent diversion, configured through set_cid_reporting_full with permanent_diversion, persists indefinitely until the driver explicitly reconfigures the device, enabling continuous data streams from touch surfaces.

Why does OpenLogi discard the first raw-XY sample from the Haptic panel?

Capacitive touch sensors typically report a large positional delta upon initial contact as the baseline establishes. According to the source comments in reprog_controls.rs, OpenLogi discards this first sample to prevent the "jump" from being interpreted as an intentional swipe gesture, ensuring accurate scroll and gesture recognition.

Can SmartShift work without raw-XY reporting enabled?

No. SmartShift wheel-mode toggling requires raw_xy = true in the set_cid_reporting call to receive rawXYEvent packets containing wheel-delta values. Without raw-XY reporting, the driver cannot detect wheel movement magnitude or direction while the button is held, making mode determination impossible.

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 →