[Bug]: Lift (B031) gesture button (HID++ CID 0x00d7) is never captured — GESTURE_BUTTON_CID hardcoded to 0x00c3
- Dominant language
- Rust
- Stars
- 21k
- Forks
- 675
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 172
Description
# [Bug]: Lift (B031) gesture button (HID++ CID 0x00d7) is never captured — GESTURE_BUTTON_CID hardcoded to 0x00c3
- **Component:** Both (GUI + agent)
- **OpenLogi version:** 0.8.3 (`openlogi --version` → openlogi 0.8.3)
- **Operating system:** Windows
- **OS version & architecture:** Windows 11, build 26200 (x64)
- **Device model:** Logitech Lift Vertical Ergonomic Mouse (model-b031/mx_vertical_mini)
- **How is the device connected?** Logi Bolt receiver (also reproduced over Bluetooth-direct and wired USB)
## Pre-flight checklist
- [x] I searched [existing issues](https://github.com/AprilNEA/OpenLogi/issues?q=is%3Aissue) and this is not a duplicate.
- [x] I am on the [latest release](https://github.com/AprilNEA/OpenLogi/releases/latest) or a recent `master` build.
- [x] I quit **Logi Options+** before running OpenLogi (the two apps fight over HID++ access and only one can own a receiver at a time).
## Affected area(s)
- [x] Button remapping
- [x] Settings / configuration (TOML)
- [x] Other — HID++ gesture-source capture (`gesture_sources` always 0)
## What happened?
Configuring the Lift's **GestureButton** in gesture mode (5-direction map, e.g. `Up = "MissionControl"`, `Click = "AppExpose"`) never produces any gesture input. The agent's capture session reports **`gesture_sources=0 gesture_buttons=0` for every connection type (Bluetooth-direct, Bolt receiver, wired USB) and even after a fully clean agent restart**. The dedicated round gesture button on the Lift is therefore dead: no HID++ raw-XY capture starts for it, and no bound action ever fires from it.
The root cause is visible in the 0.8.3 source:
- `crates/openlogi-device/src/reprog_controls.rs:43` hardcodes the dedicated gesture-button control id: `pub const GESTURE_BUTTON_CID: u16 = 0x00c3;` (the **MX-line** gesture button).
- `crates/openlogi-device/src/session/gesture.rs:193` defines `GESTURE_SOURCE_BUTTONS = [(GESTURE_BUTTON_CID, GestureButton), (HAPTIC_PANEL_CID, HapticPanel)]`, so the capture plan asks the device to divert **0x00c3** for a GestureButton in gesture mode.
- The Lift (B031) exposes its gesture source as **0x00d7** instead. From `openlogi diag controls`:
```text
device: LIFT VERTICAL ERGONOMIC MOUSE (direct 046d:b031)
cid task flags capabilities
0x0050 0x0038 0x0401 analytics-events
0x0051 0x0039 0x0401 analytics-events
0x0052 0x003a 0x0531 divertable, raw-xy, analytics-events
0x0053 0x003c 0x0d31 divertable, raw-xy, analytics-events, raw-wheel
0x0056 0x003e 0x0d31 divertable, raw-xy, analytics-events, raw-wheel
0x00fd 0x00d2 0x0531 divertable, raw-xy, analytics-events
0x00d7 0x00b4 0x03a0 divertable, raw-xy, force-raw-xy <-- dedicated gesture source (not 0x00c3)
```
- In `crates/openlogi-device/src/session/gesture.rs`, the arm loop only diverts CIDs the device actually exposes with `supports_raw_xy()`: `for &cid in &spec.divert_gesture_sources { if controls.iter().any(|c| c.cid == cid && c.supports_raw_xy()) { ... } }`. Since the Lift's control table contains **no 0x00c3**, nothing gets armed → `gesture_sources=0`.
- Control-table lookup itself is healthy on this device: the Lift's DPI/ModeShift control **0x00fd** *is* in `DPI_MODE_SHIFT_CIDS = [0x00c4, 0x00ed, 0x00fd]`, and the session reliably captures it (`dpi_buttons=1`). The gap is specific to the gesture-source CID table.
This is platform-independent logic — it should equally affect macOS/Linux users of the Lift, not just Windows.
## Steps to reproduce
1. Run OpenLogi 0.8.3 on Windows; quit Logi Options+.
2. In the GUI, set the Lift's **GestureButton** to gesture mode with any directions (e.g. Up = MissionControl, Click = AppExpose).
3. Press the round gesture button below the wheel and swipe in a direction.
4. Observe: nothing fires, and the agent log shows the capture session never arms a gesture source:
```text
INFO openlogi_device::session::gesture: control capture active index=1 gesture_sources=0 gesture_buttons=0 dpi_buttons=0 buttons=0 thumbwheel=false ...
```
## Expected behavior
The Lift's dedicated gesture source (0x00d7) should be diverted with raw-XY reporting when GestureButton is in gesture mode, `gesture_sources` should be ≥ 1, and hold-and-swipe should fire the bound directional actions — matching MX-line devices.
## Suggested fix direction
- The `GESTURE_SOURCE_BUTTONS` table needs a Lift-family entry mapping the **0x00d7** control (the only control with `force-raw-xy` here) to `ButtonId::GestureButton` — either as an additional known CID or as a per-model lookup.
## Diagnostics report
```markdown
### OpenLogi Diagnostics
**App**
- OpenLogi (GUI): v0.8.3 (release)
- Agent: v0.8.3 (connected)
- OS: Windows (x86_64)
- Locale: en-US (UI: follow system)
- Accessibility: granted · Input hook: installed
- Launch at login: yes · Menu bar: yes · Update check: on
**Devices (2)**
- LIFT VERTICAL ERGONOMIC MOUSE — mouse
- Connection: Logi Bolt receiver (slot 2) · Online: yes · Battery: ~70%
- Capabilities: buttons=yes, pointer=yes, lighting=no, hires_wheel=no, thumbwheel=no
- Model: b031 (model-ids: b031/0000/0000, ext-model: 02)
- Transports: BTLE
- Render: mx_vertical_mini
- G304 Lightspeed Wireless Gaming Mouse — mouse (unaffected by this report)
**Receivers (1)**
- Logi Bolt Receiver (VID 046d / PID c548)
```
### `openlogi list` output
```text
(inventory read from the running agent)
Logi Bolt Receiver (vid=046d pid=c548, receiver-uid=)
└─ slot 2 ● LIFT (mouse, wpid=b031, battery=70% full (discharging))
model_ids=[b031,0000,0000] ext=02 serial= unit_id= transports=btle
```
### Config (bindings excerpt)
```toml
[devices."serial:".bindings.GestureButton]
Up = "MissionControl"
Down = "ShowDesktop"
Left = "NextDesktop"
Right = "NextTab"
Click = "AppExpose"
```
### Logs
```text
INFO openlogi_device::session::gesture: control capture active index=255 gesture_sources=0 gesture_buttons=0 dpi_buttons=1 buttons=0 thumbwheel=false wake_rearm=true # BTLE-direct
INFO openlogi_device::session::gesture: control capture active index=1 gesture_sources=0 gesture_buttons=0 dpi_buttons=0 buttons=0 thumbwheel=false wake_rearm=true # Bolt receiver
INFO openlogi_device::session::gesture: control capture active index=1 gesture_sources=0 gesture_buttons=0 dpi_buttons=0 buttons=0 thumbwheel=false wake_rearm=true # fresh agent restart (15:07)
INFO openlogi_agent_core::runtime::hook: gesture click → executing bound action button=Back action=Screenshot # only clicks ever fire
(no "gesture swipe → executing bound action" line has ever appeared on this machine)
```
## Related observations
- OS-hook gesture buttons (Back/Forward in gesture mode via `WH_MOUSE_LL`) also never commit a swipe on this machine — click arm works, no `gesture swipe` log line ever. A separate probe installing a `WH_MOUSE_LL` hook confirmed the OS delivers un-injected `WM_MOUSEMOVE` deltas here, so this is probably a second, distinct issue (see also #918, #946, #471).
- The OS hook receives standard button-4/5 events from this device (dispatched as Back/Forward), so single-action bindings on those buttons work fine (verified in the log above). Whether they physically map to dedicated side buttons or to the round gesture control is not part of this report.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by reading crates/openlogi-device/src/reprog_controls.rs and crates/openlogi-device/src/session/gesture.rs, then run openlogi diag controls on a Lift to confirm its exposed controls. Trace how GESTURE_SOURCE_BUTTONS and supports_raw_xy() build the capture plan. Done means the Lift's 0x00d7 source is armed in gesture mode and directional actions can be triggered.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- embedded-iot
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100