How OpenLogi Implements Gesture Recognition on Buttons: A Technical Deep Dive
OpenLogi implements gesture recognition by diverting dedicated mouse buttons into raw‑XY reporting mode, accumulating incremental motion deltas while the button is held, and emitting typed GestureDirection events when swipe thresholds are crossed or falling back to plain clicks when released without motion.
OpenLogi is an open‑source Rust project that enables advanced input handling for Logitech HID++ devices. The gesture recognition system transforms physical button holds into directional swipe commands by intercepting low‑level device reports and converting them into high‑level UI events.
The Gesture Recognition Pipeline
The implementation spans three main stages across the openlogi‑device and openlogi‑core crates. The system first identifies which physical buttons act as gesture sources, then captures raw motion data while those buttons are held, and finally converts accumulated deltas into logical direction commands.
Identifying Gesture Source Controls
In crates/openlogi-device/src/session/gesture.rs, the constant GESTURE_SOURCE_BUTTONS (lines 188‑196) defines the HID++ control IDs that function as gesture inputs. This includes the generic gesture button found on many Logitech mice and the haptic panel button present on the MX Master 4.
Diverting to Raw‑XY Mode
The run_capture_session function opens a dedicated HID++ channel and arms the identified sources for raw‑XY reporting via arm_reprog_control. When the user holds a gesture button, the device streams incremental X/Y deltas as 0x1b04 reports. The session registers a message listener that forwards these reports to handle_reprog_with_gesture_buttons, which updates the internal CaptureAccum state (lines 327‑340).
Motion Accumulation and Threshold Detection
While a gesture button remains held, handle_raw_xy (lines 53‑59) receives delta values (dx, dy) and feeds them into a SwipeAccumulator. The accumulator tracks cumulative motion and determines when the user has performed an intentional swipe versus incidental movement. Once the accumulated displacement crosses the configured threshold, the system returns a specific GestureDirection.
The GestureDirection Enum and Event Types
The logical output types are defined in crates/openlogi-core/src/binding/gesture.rs (lines 15‑27). The GestureDirection enum provides five variants: Up, Down, Left, Right, and Click. Each variant includes stable string keys for localization and UI labeling.
When the accumulator detects a valid swipe or the button releases without sufficient motion, the system emits a CapturedInput::Gesture(button_id, direction) event. This high‑level structure decouples the raw hardware protocol from the application layer.
Dispatching to User Actions
The GUI layer receives CapturedInput events via an unbounded channel from the agent watcher in crates/openlogi-agent-core/src/watchers/gesture.rs. For each incoming gesture event, the UI looks up the user‑defined binding for the specific ButtonId and GestureDirection pair, then executes the associated action from the binding map defined in crates/openlogi-core/src/binding/binding.rs.
Implementation Example
To integrate gesture recognition in your OpenLogi workflow, create a capture specification and handle the resulting events:
use openlogi_device::session::{CaptureSpec, run_capture_session};
use openlogi_core::binding::gesture::GestureDirection;
use openlogi_core::input::CapturedInput;
// Configure which buttons to treat as gesture sources
let spec = CaptureSpec {
capture_thumbwheel: false,
divert_gesture_sources: vec![reprog_controls::GESTURE_BUTTON_CID],
divert_gesture_buttons: vec![],
divert_buttons: vec![],
};
// Spawn the capture session
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
tokio::spawn(async move {
run_capture_session(
backend,
route,
spec,
tx,
shutdown_rx,
channel_slot,
device_io,
).await.expect("Capture session failed");
});
// Process high-level gestures in your UI loop
while let Some(input) = rx.recv().await {
if let CapturedInput::Gesture(btn, dir) = input {
match dir {
GestureDirection::Up => println!("Button {:?} swiped up", btn),
GestureDirection::Click => println!("Button {:?} clicked", btn),
_ => println!("Button {:?} gesture: {:?}", btn, dir),
}
// Map (btn, dir) to user-configured actions
}
}
Summary
- OpenLogi identifies gesture‑capable buttons using the
GESTURE_SOURCE_BUTTONSconstant ingesture.rs, covering both standard gesture buttons and MX Master 4 haptic panels. - The
run_capture_sessionfunction diverts these controls into raw‑XY mode, capturing0x1b04HID++ reports viahandle_reprog_with_gesture_buttons. - Raw deltas accumulate in the
SwipeAccumulatoruntil crossing a threshold, triggeringhandle_raw_xyto emit aGestureDirection. - The system supports five gesture types defined in
GestureDirection: Up, Down, Left, Right, and Click. - High‑level
CapturedInput::Gestureevents flow through channels to the GUI, which looks up bindings in the per‑button gesture map to execute user‑defined actions.
Frequently Asked Questions
What hardware does OpenLogi support for gesture recognition?
OpenLogi supports any Logitech device that exposes gesture‑capable buttons through the HID++ protocol. This includes mice with dedicated gesture buttons and the MX Master 4 series with its haptic panel, as identified by the control IDs listed in GESTURE_SOURCE_BUTTONS (lines 188‑196 of crates/openlogi-device/src/session/gesture.rs).
How does OpenLogi distinguish between a click and a swipe?
The SwipeAccumulator tracks incremental X/Y motion while the gesture button remains held. If the accumulated displacement exceeds the configured threshold before the button releases, the system emits a directional swipe; otherwise, it falls back to a GestureDirection::Click when the hold ends without sufficient motion.
Can I customize the swipe sensitivity thresholds?
The threshold logic resides within the SwipeAccumulator implementation used by handle_raw_xy (lines 53‑59). While the core recognition pipeline handles the accumulation math, the threshold parameters are typically configurable through the CaptureSpec structure or device‑specific calibration settings in the OpenLogi configuration.
What is the difference between divert_gesture_sources and divert_gesture_buttons in the capture specification?
divert_gesture_sources accepts HID++ control IDs that should enter raw‑XY reporting mode for gesture recognition, such as the dedicated gesture button CID. divert_gesture_buttons handles standard button diversions that do not require raw motion tracking. For full gesture support, you must populate divert_gesture_sources with the appropriate control identifiers from GESTURE_SOURCE_BUTTONS.
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 →