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: Contains0x00c4(primary SmartShift button) with fallbacks0x00edand0x00fdHAPTIC_PANEL_CID: Maps to0x00c3or0x00c5for 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/0x00c5to provide continuous raw-XY streaming for ratchet-free scrolling. - Both mechanisms rely on
ReprogControlsV4feature ID0x1b04and theset_cid_reportingAPI, 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →