feat(devices): MX Master 4 firmware-first layout metadata#170
Open
hughesyadaddy wants to merge 3 commits into
Open
feat(devices): MX Master 4 firmware-first layout metadata#170hughesyadaddy wants to merge 3 commits into
hughesyadaddy wants to merge 3 commits into
Conversation
This was referenced May 14, 2026
f445f66 to
196c29f
Compare
Extend the per-device catalog with the metadata follow-up PRs need to drive the MX Master 4's two thumb-area buttons. None of this changes runtime behavior yet; consumers land in the HID listener, engine, and mouse-hook PRs. MX_MASTER_4_BUTTONS adds `thumb_button` to the standard MX Master button set so the relocated Mouse Gesture Button (CID 0x00C3) has its own UI mapping target. LogiDeviceSpec gains `has_hires_wheel`, `has_thumbwheel`, `gesture_via_sense_panel`, and `thumb_button_cid` so device entries declare HID++ feature presence and CID roles up front. ConnectedDeviceInfo mirrors those four plus runtime-state siblings (`hires_wheel_active`, `thumbwheel_active`, `active_gesture_cid`, `thumb_button_via_hid`) so platform mouse hooks read state without re-walking the capability inventory inline. `build_connected_device_info` accepts the new state kwargs, folds in spec hints only when the caller did not provide a value, and normalizes `active_gesture_cid` to int. Naming follows Logitech's firmware/marketing terminology (verified against Solaar `special_keys.py`, the MX Master 4 reprog-controls dump in pwr-Solaar/Solaar#2964, and Logitech's own MX Master 4 marketing): 0x01A0 is the haptic Sense Panel, 0x00C3 is the relocated Mouse Gesture Button. "Action Ring" is a Logi Options+ software overlay invoked by pressing the Sense Panel, not a hardware button, so internal symbols use `thumb_button` / `gesture_via_sense_panel` instead. The MX Master 4 catalog entry lists `gesture_cids` with 0x01A0 first so the listener prefers diverting the larger Sense Panel with rawXY, points `thumb_button_cid` at 0x00C3 so the relocated Mouse Gesture Button is wired as a button-only extra, and sets `gesture_via_sense_panel = True` so the device is eligible for an OS-level fallback swap when firmware rejects the panel divert. Both CIDs are confirmed divertable + rawXY- capable on real MX Master 4 firmware (Solaar issue #2964). `tests/test_logi_devices.py` updates the haptic-control test to assert `thumb_button` is in `supported_buttons` while the Sense Panel CID still produces no UI button entry, and adds tests for spec-to-runtime mirroring and `active_gesture_cid` int normalization.
…back tests - Replace ``bool`` capability defaults in ``build_connected_device_info`` with ``bool | None``. ``None`` means "no probe performed" and defers to the catalog hint; explicit ``True``/``False`` is definitive runtime evidence. Prior behavior silently dropped a definitive ``False`` runtime probe under an optimistic catalog ``True``, which would let the listener try to divert a feature the firmware did not actually expose. - Extract ``_coerce_cid`` so caller-supplied CIDs (int, ``"0x01A0"`` hex string, ``None``) all funnel through one normalizer; downstream consumers no longer compare ``int`` against ``str`` and silently miss matches. - Stop letting an empty ``gesture_cids=()`` collapse into the spec default via short-circuit ``or``. An empty tuple is the runtime's explicit "I saw none"; treat it as truth. New tests cover: hex-string CID normalization, malformed CID resolves to None, unknown PID falls back to generic + no MX-family buttons, explicit runtime ``False`` overrides catalog ``True``, runtime ``True`` upgrades an unknown device, and empty ``gesture_cids`` is respected on both spec and fallback paths.
clamp_dpi used a bare int(value) cast against whatever flowed in from config.json, QML bindings, or HID reads. A user-edited config that quoted the DPI as a string, a stale None from a partial HID report, or a leaked bool flag would raise ValueError/TypeError and abort the engine's DPI path -- the cursor freezes until restart. Coerce defensively instead. Accept ints, floats, decimal/hex strings, and fall back to the device's dpi_min on anything else. Reject bool explicitly so a truthy flag never silently clamps to 0 or 1 DPI via int(True). The clamp range itself is unchanged.
ef00d18 to
b5d1805
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the catalog metadata the follow-on HID++ runtime and UI PRs depend on.
No runtime behavior changes in this commit; everything lands in the listener,
engine, and mouse-hook PRs that build on top.
core/logi_device_catalog.pyMX_MASTER_4_BUTTONS— explicit button tuple that includesthumb_buttonfor the relocated Mouse Gesture Button (CID
0x00C3). The MX Master 4 movesthis physical button to the thumb area and replaces the original gesture
surface with the larger haptic Sense Panel (CID
0x01A0).supported_buttons: MX_MASTER_4_BUTTONShas_hires_wheel: True/has_thumbwheel: True— declare HID++ featurepresence up front so the runtime skips discovery probes on every connect.
gesture_cids: (0x01A0, 0x00C3, 0x00D7)— listener tries the Sense Panelfirst; falls back to the physical button, then to the virtual gesture CID.
thumb_button_cid: 0x00C3— the relocated small button, diverted as abutton-only extra (no rawXY) alongside whichever CID wins the gesture role.
gesture_via_sense_panel: True— enables an OS-levelbtn=6/BTN_TASKfallback path for firmware that rejects the Sense Panel divert.
has_hires_wheel: Trueandhas_thumbwheel: Trueso the firmware-invert path works for those devicestoo without per-device special-casing in the listener.
core/logi_devices.pyLogiDeviceSpecgains four new optional fields:has_hires_wheel,has_thumbwheel,gesture_via_sense_panel,thumb_button_cid. All defaultto
False/Noneso existing device entries are unaffected.ConnectedDeviceInfomirrors those four plus runtime-state twins(
hires_wheel_active,thumbwheel_active,active_gesture_cid,thumb_button_via_hid) so platform mouse hooks read state directlywithout re-walking the capability inventory inline.
build_connected_device_infoaccepts the new state fields as keywordarguments with safe defaults; callers that do not pass them get the same
behavior as before.
_LAYOUT_BUTTONSmaps"mx_master_4"toMX_MASTER_4_BUTTONSsoresolve_devicereturns the correct button set for the new layout key.tests/test_logi_devices.pyExtends the existing MX Master 4 device-resolution test to assert the new
catalog fields (
gesture_via_sense_panel,thumb_button_cid,has_hires_wheel,has_thumbwheel,gesture_cids) are present and correct.Adds a round-trip test for
build_connected_device_infowith the new keywordarguments.
Testing
All existing tests pass; the new assertions cover the added fields.