Implementing the Razer viper v3 Pro LOD System - #37
Merged
snekxs merged 4 commits intoAug 8, 2026
Merged
Conversation
OpenRazer has had this open as issue #783, tagged help wanted, since 2019.
Class 0x0b is now fully mapped, and every claim below was verified at the
sensor by lifting the mouse rather than at a status byte.
read 0x0b/0x85 dataSize 0x05 echo, 00, tracking, liftOff-1, landing-1
pair 0x0b/0x05 dataSize 0x0a 00, 04, liftOff-1, landing-1, 00 x 6
setting 0x0b/0x0b dataSize 0x04 00, 04, setting, value
setting 01 = tracking level 00/01/02
setting 04 = asymmetric unlock, value 01
Three things are easy to get wrong here, so they are enforced rather than
merely documented.
The pair write is refused in symmetric mode and still moves what the read
reports. Omitting the unlock therefore produces a driver that throws, shows an
error, and then reads back exactly the values the user asked for while the
sensor holds the old ones. setLiftOff always sends the unlock first.
There is no mode bit, and nothing readable reports one. The mouse honours
whichever store was written last, so writing a tracking level is what leaves
asymmetric mode. probeAsymmetric recovers the current mode from the pair
write's own status, re-sending the values already stored so it cannot disturb
anything, and runs once per connection because it is still a write.
The firmware stores an inverted pair without complaint. One session left the
mouse holding lift-off 2 with landing 26, which the vendor software cannot
express. razerMaxLanding is the single source of that rule.
readLiftOff degrades to null rather than throwing. Class 0x0b has only been
exercised on the receiver, and an uncaught read would fail the whole status
read instead of one control.
MouseStatus gains an optional asymmetricLiftOff for the pair. liftOffDistance
is unchanged and no other driver is touched.
Mice that report asymmetricLiftOff get a Single / Asymmetric switch in the
shape the vendor software uses: one control or the other, never both. Drivers
that do not report the pair never see the switch and keep the plain three-stop
control, so no existing device changes.
Choosing a mode is itself the write, because the mouse honours whichever store
was written last. That means there is no mode flag to drift out of sync, and
no state the panel can claim while the device disagrees.
Landing is a slider bounded by lift-off rather than a number field, so an
invalid pair is not expressible. The value is clamped and the range is left
alone: a range input positions its thumb relative to its own bounds, so
narrowing max to the ceiling slid the thumb across the track whenever lift-off
moved even though the number under it had not changed. Keeping both at the
device's full range also lines the two tracks up, since they are the same 24
steps offset by one. Staging happens on release, so a drag is one change.
Two fixes to existing CSS that this uncovered:
- `.segmented.three` was never defined, so the three lift-off buttons had
been laid out on a four-column grid.
- Author-level `display` beats the browser's `[hidden]` rule, so a `hidden`
property set from script silently did nothing on anything laid out with
grid or flex. There was already a one-off patch for `.pending-bar`; this
replaces the need for more of them.
Member
|
Closing because this conflicts with the current Razer implementation and overlaps newer Viper V3 Pro work. Please rebase the LOD portion onto current dev and submit it separately. |
Contributor
Author
|
im assuming that this issue has been fixed if it was merged already yeah? |
dwei30
pushed a commit
to dwei30/openmouse
that referenced
this pull request
Aug 30, 2026
Picks up Keychron M6 (OpenMouse-Project#33), Nape Pro (OpenMouse-Project#37), Glorious Model O 2/I 2 (OpenMouse-Project#38), and SteelSeries Rival 3 (OpenMouse-Project#39) support from mouse-protocol main. The bump retires the generic KeychronHidClient in favor of KeychronNapeHidClient (a strict superset), which this app's controller.ts still imported by its old path. Also widens the Glorious lighting call site since SupportedClient now includes GloriousHidClient's stricter setLighting signature. Verified with npm ci, npm run build, and the full test suite (84/84). Co-Authored-By: Claude Sonnet 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.
Implements the lift-off distance system Synapse exposes for the Viper V3 Pro
(
1532:00c0wired,1532:00c1receiver). Nothing here touches the V4 Pro driver.Class
0x0bwas previously unmapped in any open-source project — OpenRazer hashad it open as issue #783, tagged help wanted, since 2019.
0x0b/0x850x05echo, 00, tracking, liftOff-1, landing-10x0b/0x050x0a00, 04, liftOff-1, landing-1, 00 × 60x0b/0x0b0x0400, 04, setting, value01= tracking level ·04= asymmetric unlockUI
A Single / Asymmetric switch matching the vendor software: one control or
the other, never both. Single reuses the existing three-stop
liftOffDistancecontrol unchanged. Asymmetric shows lift-off (2–26) and landing (1–25) as
sliders, where landing is bounded by lift-off so an invalid pair cannot be
expressed.
Three things worth knowing
The pair write is refused in symmetric mode and still moves what the read
reports. Omitting the unlock gives a driver that throws, then reads back
exactly the values the user asked for while the sensor holds the old ones.
setLiftOffalways sends the unlock first.There is no mode bit and nothing readable reports one. The mouse honours
whichever store was written last, so writing a tracking level is what leaves
asymmetric mode.
probeAsymmetricrecovers the mode from the pair write's ownstatus, re-sending the stored values so it cannot disturb anything, and runs
once per connection because it is still a write.
The firmware stores an inverted pair without complaint — testing left the
mouse holding lift-off 2 with landing 26, which Synapse cannot express.
razerMaxLandingis the single source of that rule, enforced at the slider,the driver, and the command builder.
Verification
Every protocol claim was confirmed by lifting the mouse, not by read-back —
on this device a rejected write still changes what the read reports, which
produced three wrong conclusions before that rule was adopted.
npm run checkis 117/117 green.Notes for review
MouseStatus.asymmetricLiftOffis a new optional field. Additive:liftOffDistanceis unchanged and no other driver is touched.0x0bhas only been exercised on the receiver.Expected wired behaviour is no lift-off card rather than an error —
readLiftOffdegrades to null so an unsupported transport cannot fail thewhole status read.