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:
WheelModeenum: DefinesFreeandRatchetstates for the wheel mechanism- Auto-disengage thresholds: Validated against
SMARTSHIFT_MIN_AUTO_DISENGAGEandSMARTSHIFT_AUTO_DISENGAGE_DEFAULTconstants - 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:
SmartShiftModeenum (lines 33-39): Represents hardware values for ratchet states with aflipped()helper method to toggle between modesSmartShiftThreshold: Wraps raw threshold values withfrom_rounded()for UI conversions andtry_new()for validationSmartShiftAutoDisengage: 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 inipc.rs): TransmitsSmartShiftStatusfrom UI to agentread_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
- OpenLogi persists SmartShift wheel settings in
crates/openlogi-core/src/config/settings.rsusing theSmartShiftstruct andWheelModeenum - HID++ protocol translation occurs in
crates/openlogi-core/src/hid/smartshift.rsviaSmartShiftModeandSmartShiftThresholdtypes - The
openlogi-ipccrate providesset_smartshiftandread_smartshiftRPC methods for agent communication - The desktop UI in
smartshift.rsmanages wheel mode, auto-disengage thresholds, and permanent ratchet states throughAppState::update_smartshift - Settings persist to
config.tomlbecause hardware stores values only in volatile RAM - CLI diagnostics are available in
crates/openlogi-cli/src/cmd/diag/smartshift.rsfor troubleshooting
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →