feat(wlmouse): DPI stage table, high-speed, turbo, angle tune, button combinations, and the mouse behind a receiver - #52
Open
ydw1904 wants to merge 2 commits into
Conversation
The mouse has always sent its whole stage table in one packet and the driver has always decoded it, but only the active stage reached the app, so the panel could edit one DPI value where the vendor software edits six. The control app already ships a generic stage editor for exactly this shape (Keychron and Teevolution use it), so this reports `dpiStages`, `activeDpiStage`, and the `dpiStageEditor` hint, and adds the three setters that editor calls. `setDpiStageCount` and `setDpiStageValue` reuse the write `setDpi` was already making: the count and every stage travel together in one packet, so a value edit is a full table write either way. Editing a stage keeps a Y that already differs from X, because the shared editor shows a single value per stage and must not quietly undo a separate-axis setup. `setActiveDpiStage` writes a command that is not in any capture: it is the `0x82` read with the high bit cleared, which is how every other command in this driver pairs up. That guess is verified by reading the active stage back, and an unknown command comes back `unsupported`, so a wrong byte surfaces as an error rather than a silent no-op. It wants a run on real hardware before anyone trusts it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
WLmouse's own configurator exposes four sensor settings this driver did not reach, and names the mouse behind a receiver that the driver could not name. All five commands are read out of the vendor's web tool, which uses the same page-command framing this driver already speaks, so nothing here is guessed: - high-speed mode page 0x01, read 0x8b / write 0x0b - turbo mode page 0x01, read 0x93 / write 0x13 - angle tune page 0x01, read 0x94 / write 0x14, signed byte - button combos page 0x03, read 0x81 / write 0x01 - paired product target 0x01, page 0x00, read 0x8b, arg 0x02 The four settings are optional per model: a mouse without one answers `unsupported` rather than failing the exchange, so the reads catch into null and the status reports "no such control" instead of breaking the connection. The paired-product read matters most for the shared 1K receiver, which enumerates under one product id whatever mouse it is paired with. Without it every mouse on that dongle reports as "WLmouse 1K receiver" — with it the driver names the model, and the app can pick artwork for it. The doubled "WLmouse WLmouse" prefix on the shared-receiver catalogue entries goes too. Turbo needs high-speed mode on before the firmware will hold it, which the read-back reports rather than silently dropping. Verified against a Beast Max on the 1K receiver. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Two commits that together bring the WLmouse driver up to what the vendor's own
configurator exposes: the full DPI stage table, four sensor settings the driver
could not reach, and the name of the mouse sitting behind a shared receiver.
1. The DPI stage table (
873597a)These mice hold six onboard stages and send the whole table in one packet, and
the driver has always decoded it — but only the active stage reached the app, so
OpenMouse could edit one DPI value where the vendor software edits six. The
control app already ships a generic stage editor for this exact shape (Keychron
M6 and Teevolution use it), so the driver now reports
dpiStages,activeDpiStageand thedpiStageEditorhint, plus the three setters thateditor calls.
setDpiStageCountandsetDpiStageValuereuse the writesetDpiwas alreadymaking: count and stages travel together in one packet, so a single-value edit
is a full table write either way. That write and its read moved into
writeStages/readStages, whichsetDpinow uses too.Editing one stage keeps a Y that already differs from X — the shared editor
shows a single value per stage and must not quietly flatten a separate-axis
setup.
One inferred byte.
setActiveDpiStagewrites profile-page command0x02,which is not in any capture. It is the
0x82read with the high bit cleared,which is how every other command in this driver pairs up (
0x80/0x00,0x81/0x01,0x84/0x04,0x88/0x08,0x89/0x09,0x8a/0x0a). Thewrite is verified by reading the active stage back, and the mouse answers an
unknown command with
unsupported, so a wrong byte surfaces as a plain errorrather than a silent no-op. A second pair of hands on hardware would be welcome
here.
2. Sensor settings and the mouse behind the receiver (
89c8f97)Read out of the vendor's web tool, which speaks the same page-command framing —
nothing guessed:
0x010x8b/0x0b0x010x93/0x130x010x94/0x140x030x81/0x010x00, target0x010x8b, arg0x02The four settings are optional per model: a mouse without one answers
unsupportedrather than failing the exchange, so the reads catch into null andthe status reports "no such control" instead of breaking the connection. Turbo
needs high-speed mode on before the firmware will hold it, which the read-back
reports rather than silently dropping.
The paired-product read matters most for the shared 1K receiver, which
enumerates under one product id whatever it is paired with. Without it every
mouse on that dongle reports as "WLmouse 1K receiver"; with it the driver names
the model and the app can pick artwork for it. The doubled "WLmouse WLmouse"
prefix on the shared-receiver catalogue entries goes too.
Testing
npm run checkclean,npm test832 passing.src/drivers/wlmouse/hid.test.tscover the stage table roundtrip, the Y-preservation rule, the rejection paths, and the optional settings
degrading to null, against a fake device that models a mutable stage table.
36a7:a882), reporting the same sixstages its vendor software shows (screenshot above).
App side: OpenMouse-Project/openmouse#162.
Per-stage DPI colours are the obvious follow-up — the command carrying them is
still unknown, so they are deliberately out of scope.
🤖 Generated with Claude Code