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

> Discover how OpenLogi's SmartShift wheel mode toggling and permanent-ratchet Haptic panels function using CID diversion and raw-XY streaming for seamless control.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: internals
- Published: 2026-08-26

---

**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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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:

```rust
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`](https://github.com/AprilNEA/OpenLogi/blob/main/reprog_controls.rs), yielding a `RawControlEvent` containing wheel-delta values:

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

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

```rust
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`](https://github.com/AprilNEA/OpenLogi/blob/main/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:**

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

```rust
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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hidpp/src/feature/reprog_controls/task_ids.rs) | Defines task IDs such as SmartShift (`0x9D`) |
| [`crates/openlogi-device/src/session/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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.