AprilNEA / AprilNEA/OpenLogi

[Feature]: allow the DPI / ModeShift button to carry the gesture role

Closed
#806 6 comments 4 reactions 0 assignees View on GitHub
area: hidpp platform: all type: feature
Dominant language
Rust
Stars
21k
Forks
675
Avg merge
2d 5h
Merged PRs (30d)
172

Description

### Pre-flight

Searched existing issues and the roadmap; I couldn't find this tracked. Raising it as a discussion rather than a PR, because it reverses what looks like a deliberate design decision — happy to be told it's deliberate and stays that way.

### Problem / motivation

The gesture role is currently restricted to two controls:

```rust
pub fn is_hidpp_gesture_source(self) -> bool {
matches!(self, ButtonId::GestureButton | ButtonId::HapticPanel)
}
```

`GESTURE_SOURCE_BUTTONS` correspondingly lists only `0x00c3` and the haptic panel.

An MX Anywhere 3 has neither. Its `0x1b04` table is:

```
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
0x00c4 0x009d 0x0531 divertable, raw-xy, analytics-events
0x00d7 0x00b4 0x03a0 divertable, raw-xy, force-raw-xy
```

So on this device the only gesture-capable route is the OS-hook path on Middle/Back/Forward. Middle click is a reasonable home for gestures, but it's also the natural home for `ToggleSmartShift` on a mouse with no dedicated gesture button, and you can't have both.

Worth noting `0x00c4` advertises exactly the same flags as middle click — `0x0531`, including `raw-xy`.

### Proposed solution

Let the DPI / ModeShift family carry the gesture role on devices that advertise raw-XY on it, so a mouse without a dedicated gesture button has somewhere to put gestures other than its middle button.

I've run this locally on 0.7.4 and it works — all four directions dispatch correctly from `0x00c4` on an MX Anywhere 3, so the control does stream raw-XY while held, not just advertise it. Three changes were needed:

1. `is_hidpp_gesture_source()` to include `ButtonId::DpiToggle`.
2. `GESTURE_SOURCE_BUTTONS` to map the `DPI_MODE_SHIFT_CIDS` to `ButtonId::DpiToggle`.
3. The DPI arming loop in `run_capture_session` to skip a CID already armed as a gesture source — otherwise it re-arms it with `raw_xy = false` and strips the raw-XY reporting, the same hazard the `divert_buttons` loop already guards against.

Graceful degradation seems to come for free: the gesture divert loop checks `supports_raw_xy()` first, so a device that doesn't advertise it never arms as a gesture source and keeps the existing plain-divert behaviour.

### Things I'm unsure about

- Whether other members of `DPI_MODE_SHIFT_CIDS` (`0x00ed`, `0x00fd`) behave the same way. I've only tested `0x00c4`.
- Whether losing the plain DPI-toggle press when that button is in gesture mode is an acceptable trade. It seemed reasonable to me — the Click direction can be bound to `CycleDpiPresets` — but it is a behaviour change for anyone who gestures on it.
- Whether there's a device-capability reason for the current restriction that isn't visible in the `0x1b04` flags, in which case this is the wrong approach.

Happy to open a PR if the direction is welcome. Tested on macOS 27 (arm64), MX Anywhere 3 over Bluetooth LE.

Related: #805 (unattributed `OtherMouse*` events on macOS), which had to be fixed first before gestures worked on this hardware at all.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with ButtonId::is_hidpp_gesture_source and GESTURE_SOURCE_BUTTONS, then trace the DPI arming loop in run_capture_session alongside the existing divert-buttons guard. Confirm the tested 0x00c4 path and the other DPI_MODE_SHIFT_CIDS behavior before deciding how raw-XY capability and plain DPI-toggle behavior should work.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
desktop
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.