@openmouse/protocol 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +167 -0
- package/README.md +89 -0
- package/dist/asus/index.d.ts +52 -0
- package/dist/asus/index.d.ts.map +1 -0
- package/dist/asus/index.js +248 -0
- package/dist/asus/index.js.map +1 -0
- package/dist/atk/index.d.ts +200 -0
- package/dist/atk/index.d.ts.map +1 -0
- package/dist/atk/index.js +480 -0
- package/dist/atk/index.js.map +1 -0
- package/dist/bitmouse/index.d.ts +284 -0
- package/dist/bitmouse/index.d.ts.map +1 -0
- package/dist/bitmouse/index.js +396 -0
- package/dist/bitmouse/index.js.map +1 -0
- package/dist/compx/codec.d.ts +31 -0
- package/dist/compx/codec.d.ts.map +1 -0
- package/dist/compx/codec.js +50 -0
- package/dist/compx/codec.js.map +1 -0
- package/dist/corsair/index.d.ts +147 -0
- package/dist/corsair/index.d.ts.map +1 -0
- package/dist/corsair/index.js +223 -0
- package/dist/corsair/index.js.map +1 -0
- package/dist/dareu/index.d.ts +136 -0
- package/dist/dareu/index.d.ts.map +1 -0
- package/dist/dareu/index.js +344 -0
- package/dist/dareu/index.js.map +1 -0
- package/dist/drivers/asus/hid.d.ts +29 -0
- package/dist/drivers/asus/hid.d.ts.map +1 -0
- package/dist/drivers/asus/hid.js +419 -0
- package/dist/drivers/asus/hid.js.map +1 -0
- package/dist/drivers/atk/bitmouse-hid.d.ts +80 -0
- package/dist/drivers/atk/bitmouse-hid.d.ts.map +1 -0
- package/dist/drivers/atk/bitmouse-hid.js +501 -0
- package/dist/drivers/atk/bitmouse-hid.js.map +1 -0
- package/dist/drivers/atk/hid.d.ts +163 -0
- package/dist/drivers/atk/hid.d.ts.map +1 -0
- package/dist/drivers/atk/hid.js +1368 -0
- package/dist/drivers/atk/hid.js.map +1 -0
- package/dist/drivers/atk/products.d.ts +13 -0
- package/dist/drivers/atk/products.d.ts.map +1 -0
- package/dist/drivers/atk/products.js +13 -0
- package/dist/drivers/atk/products.js.map +1 -0
- package/dist/drivers/attackshark/dpi.d.ts +41 -0
- package/dist/drivers/attackshark/dpi.d.ts.map +1 -0
- package/dist/drivers/attackshark/dpi.js +215 -0
- package/dist/drivers/attackshark/dpi.js.map +1 -0
- package/dist/drivers/attackshark/hid.d.ts +124 -0
- package/dist/drivers/attackshark/hid.d.ts.map +1 -0
- package/dist/drivers/attackshark/hid.js +728 -0
- package/dist/drivers/attackshark/hid.js.map +1 -0
- package/dist/drivers/corsair/hid.d.ts +70 -0
- package/dist/drivers/corsair/hid.d.ts.map +1 -0
- package/dist/drivers/corsair/hid.js +414 -0
- package/dist/drivers/corsair/hid.js.map +1 -0
- package/dist/drivers/dareu/hid.d.ts +59 -0
- package/dist/drivers/dareu/hid.d.ts.map +1 -0
- package/dist/drivers/dareu/hid.js +478 -0
- package/dist/drivers/dareu/hid.js.map +1 -0
- package/dist/drivers/endgame/egg-op1-hid.d.ts +107 -0
- package/dist/drivers/endgame/egg-op1-hid.d.ts.map +1 -0
- package/dist/drivers/endgame/egg-op1-hid.js +605 -0
- package/dist/drivers/endgame/egg-op1-hid.js.map +1 -0
- package/dist/drivers/endgame/egg-we-control.d.ts +42 -0
- package/dist/drivers/endgame/egg-we-control.d.ts.map +1 -0
- package/dist/drivers/endgame/egg-we-control.js +87 -0
- package/dist/drivers/endgame/egg-we-control.js.map +1 -0
- package/dist/drivers/endgame/egg-we-hid.d.ts +58 -0
- package/dist/drivers/endgame/egg-we-hid.d.ts.map +1 -0
- package/dist/drivers/endgame/egg-we-hid.js +514 -0
- package/dist/drivers/endgame/egg-we-hid.js.map +1 -0
- package/dist/drivers/fantech/hid.d.ts +106 -0
- package/dist/drivers/fantech/hid.d.ts.map +1 -0
- package/dist/drivers/fantech/hid.js +295 -0
- package/dist/drivers/fantech/hid.js.map +1 -0
- package/dist/drivers/finalmouse/hid.d.ts +31 -0
- package/dist/drivers/finalmouse/hid.d.ts.map +1 -0
- package/dist/drivers/finalmouse/hid.js +181 -0
- package/dist/drivers/finalmouse/hid.js.map +1 -0
- package/dist/drivers/gearhub/hid.d.ts +191 -0
- package/dist/drivers/gearhub/hid.d.ts.map +1 -0
- package/dist/drivers/gearhub/hid.js +542 -0
- package/dist/drivers/gearhub/hid.js.map +1 -0
- package/dist/drivers/glorious/classic-hid.d.ts +62 -0
- package/dist/drivers/glorious/classic-hid.d.ts.map +1 -0
- package/dist/drivers/glorious/classic-hid.js +326 -0
- package/dist/drivers/glorious/classic-hid.js.map +1 -0
- package/dist/drivers/glorious/hid.d.ts +39 -0
- package/dist/drivers/glorious/hid.d.ts.map +1 -0
- package/dist/drivers/glorious/hid.js +203 -0
- package/dist/drivers/glorious/hid.js.map +1 -0
- package/dist/drivers/gwolves/hid.d.ts +27 -0
- package/dist/drivers/gwolves/hid.d.ts.map +1 -0
- package/dist/drivers/gwolves/hid.js +225 -0
- package/dist/drivers/gwolves/hid.js.map +1 -0
- package/dist/drivers/gwolves/products.d.ts +17 -0
- package/dist/drivers/gwolves/products.d.ts.map +1 -0
- package/dist/drivers/gwolves/products.js +58 -0
- package/dist/drivers/gwolves/products.js.map +1 -0
- package/dist/drivers/hyperx/hid.d.ts +59 -0
- package/dist/drivers/hyperx/hid.d.ts.map +1 -0
- package/dist/drivers/hyperx/hid.js +229 -0
- package/dist/drivers/hyperx/hid.js.map +1 -0
- package/dist/drivers/incott/hid.d.ts +513 -0
- package/dist/drivers/incott/hid.d.ts.map +1 -0
- package/dist/drivers/incott/hid.js +1173 -0
- package/dist/drivers/incott/hid.js.map +1 -0
- package/dist/drivers/index.d.ts +5 -0
- package/dist/drivers/index.d.ts.map +1 -0
- package/dist/drivers/index.js +5 -0
- package/dist/drivers/index.js.map +1 -0
- package/dist/drivers/keychron/m6-hid.d.ts +81 -0
- package/dist/drivers/keychron/m6-hid.d.ts.map +1 -0
- package/dist/drivers/keychron/m6-hid.js +486 -0
- package/dist/drivers/keychron/m6-hid.js.map +1 -0
- package/dist/drivers/keychron/nape-hid.d.ts +60 -0
- package/dist/drivers/keychron/nape-hid.d.ts.map +1 -0
- package/dist/drivers/keychron/nape-hid.js +432 -0
- package/dist/drivers/keychron/nape-hid.js.map +1 -0
- package/dist/drivers/ksnake/hid.d.ts +74 -0
- package/dist/drivers/ksnake/hid.d.ts.map +1 -0
- package/dist/drivers/ksnake/hid.js +368 -0
- package/dist/drivers/ksnake/hid.js.map +1 -0
- package/dist/drivers/lamzu/hid.d.ts +58 -0
- package/dist/drivers/lamzu/hid.d.ts.map +1 -0
- package/dist/drivers/lamzu/hid.js +414 -0
- package/dist/drivers/lamzu/hid.js.map +1 -0
- package/dist/drivers/lamzu-atlantis/hid.d.ts +148 -0
- package/dist/drivers/lamzu-atlantis/hid.d.ts.map +1 -0
- package/dist/drivers/lamzu-atlantis/hid.js +587 -0
- package/dist/drivers/lamzu-atlantis/hid.js.map +1 -0
- package/dist/drivers/logitech/bolt.d.ts +41 -0
- package/dist/drivers/logitech/bolt.d.ts.map +1 -0
- package/dist/drivers/logitech/bolt.js +105 -0
- package/dist/drivers/logitech/bolt.js.map +1 -0
- package/dist/drivers/logitech/hidpp.d.ts +525 -0
- package/dist/drivers/logitech/hidpp.d.ts.map +1 -0
- package/dist/drivers/logitech/hidpp.js +2774 -0
- package/dist/drivers/logitech/hidpp.js.map +1 -0
- package/dist/drivers/logitech/mode-status.d.ts +53 -0
- package/dist/drivers/logitech/mode-status.d.ts.map +1 -0
- package/dist/drivers/logitech/mode-status.js +52 -0
- package/dist/drivers/logitech/mode-status.js.map +1 -0
- package/dist/drivers/logitech/onboard-profiles.d.ts +298 -0
- package/dist/drivers/logitech/onboard-profiles.d.ts.map +1 -0
- package/dist/drivers/logitech/onboard-profiles.js +965 -0
- package/dist/drivers/logitech/onboard-profiles.js.map +1 -0
- package/dist/drivers/logitech/rgb-effects.d.ts +20 -0
- package/dist/drivers/logitech/rgb-effects.d.ts.map +1 -0
- package/dist/drivers/logitech/rgb-effects.js +93 -0
- package/dist/drivers/logitech/rgb-effects.js.map +1 -0
- package/dist/drivers/mchose/a5-gen1-hid.d.ts +36 -0
- package/dist/drivers/mchose/a5-gen1-hid.d.ts.map +1 -0
- package/dist/drivers/mchose/a5-gen1-hid.js +219 -0
- package/dist/drivers/mchose/a5-gen1-hid.js.map +1 -0
- package/dist/drivers/mchose/dock-hid.d.ts +26 -0
- package/dist/drivers/mchose/dock-hid.d.ts.map +1 -0
- package/dist/drivers/mchose/dock-hid.js +176 -0
- package/dist/drivers/mchose/dock-hid.js.map +1 -0
- package/dist/drivers/mchose/hid.d.ts +118 -0
- package/dist/drivers/mchose/hid.d.ts.map +1 -0
- package/dist/drivers/mchose/hid.js +550 -0
- package/dist/drivers/mchose/hid.js.map +1 -0
- package/dist/drivers/mchose/v3-hid.d.ts +109 -0
- package/dist/drivers/mchose/v3-hid.d.ts.map +1 -0
- package/dist/drivers/mchose/v3-hid.js +572 -0
- package/dist/drivers/mchose/v3-hid.js.map +1 -0
- package/dist/drivers/microsoft/hid.d.ts +25 -0
- package/dist/drivers/microsoft/hid.d.ts.map +1 -0
- package/dist/drivers/microsoft/hid.js +247 -0
- package/dist/drivers/microsoft/hid.js.map +1 -0
- package/dist/drivers/moddo/hid.d.ts +31 -0
- package/dist/drivers/moddo/hid.d.ts.map +1 -0
- package/dist/drivers/moddo/hid.js +154 -0
- package/dist/drivers/moddo/hid.js.map +1 -0
- package/dist/drivers/mouse-types.d.ts +388 -0
- package/dist/drivers/mouse-types.d.ts.map +1 -0
- package/dist/drivers/mouse-types.js +2 -0
- package/dist/drivers/mouse-types.js.map +1 -0
- package/dist/drivers/ninjutso/hid.d.ts +62 -0
- package/dist/drivers/ninjutso/hid.d.ts.map +1 -0
- package/dist/drivers/ninjutso/hid.js +680 -0
- package/dist/drivers/ninjutso/hid.js.map +1 -0
- package/dist/drivers/orbital/hid.d.ts +24 -0
- package/dist/drivers/orbital/hid.d.ts.map +1 -0
- package/dist/drivers/orbital/hid.js +85 -0
- package/dist/drivers/orbital/hid.js.map +1 -0
- package/dist/drivers/orbital/host-protocol.d.ts +59 -0
- package/dist/drivers/orbital/host-protocol.d.ts.map +1 -0
- package/dist/drivers/orbital/host-protocol.js +884 -0
- package/dist/drivers/orbital/host-protocol.js.map +1 -0
- package/dist/drivers/pulsar/pulsar-hid.d.ts +63 -0
- package/dist/drivers/pulsar/pulsar-hid.d.ts.map +1 -0
- package/dist/drivers/pulsar/pulsar-hid.js +381 -0
- package/dist/drivers/pulsar/pulsar-hid.js.map +1 -0
- package/dist/drivers/pulsar/pulsar-pro-hid.d.ts +46 -0
- package/dist/drivers/pulsar/pulsar-pro-hid.d.ts.map +1 -0
- package/dist/drivers/pulsar/pulsar-pro-hid.js +343 -0
- package/dist/drivers/pulsar/pulsar-pro-hid.js.map +1 -0
- package/dist/drivers/pulsar/pulsar-xs1-hid.d.ts +40 -0
- package/dist/drivers/pulsar/pulsar-xs1-hid.d.ts.map +1 -0
- package/dist/drivers/pulsar/pulsar-xs1-hid.js +251 -0
- package/dist/drivers/pulsar/pulsar-xs1-hid.js.map +1 -0
- package/dist/drivers/rawm/hid.d.ts +29 -0
- package/dist/drivers/rawm/hid.d.ts.map +1 -0
- package/dist/drivers/rawm/hid.js +210 -0
- package/dist/drivers/rawm/hid.js.map +1 -0
- package/dist/drivers/razer/cobra-hid.d.ts +63 -0
- package/dist/drivers/razer/cobra-hid.d.ts.map +1 -0
- package/dist/drivers/razer/cobra-hid.js +282 -0
- package/dist/drivers/razer/cobra-hid.js.map +1 -0
- package/dist/drivers/razer/hid-open.d.ts +13 -0
- package/dist/drivers/razer/hid-open.d.ts.map +1 -0
- package/dist/drivers/razer/hid-open.js +29 -0
- package/dist/drivers/razer/hid-open.js.map +1 -0
- package/dist/drivers/razer/hid.d.ts +231 -0
- package/dist/drivers/razer/hid.d.ts.map +1 -0
- package/dist/drivers/razer/hid.js +772 -0
- package/dist/drivers/razer/hid.js.map +1 -0
- package/dist/drivers/razer/viper-hid.d.ts +33 -0
- package/dist/drivers/razer/viper-hid.d.ts.map +1 -0
- package/dist/drivers/razer/viper-hid.js +177 -0
- package/dist/drivers/razer/viper-hid.js.map +1 -0
- package/dist/drivers/razer/viper-mini-hid.d.ts +58 -0
- package/dist/drivers/razer/viper-mini-hid.d.ts.map +1 -0
- package/dist/drivers/razer/viper-mini-hid.js +273 -0
- package/dist/drivers/razer/viper-mini-hid.js.map +1 -0
- package/dist/drivers/razer/viper-v4-pro-hid.d.ts +26 -0
- package/dist/drivers/razer/viper-v4-pro-hid.d.ts.map +1 -0
- package/dist/drivers/razer/viper-v4-pro-hid.js +154 -0
- package/dist/drivers/razer/viper-v4-pro-hid.js.map +1 -0
- package/dist/drivers/registry.d.ts +70 -0
- package/dist/drivers/registry.d.ts.map +1 -0
- package/dist/drivers/registry.js +146 -0
- package/dist/drivers/registry.js.map +1 -0
- package/dist/drivers/steelseries/aerox3-hid.d.ts +53 -0
- package/dist/drivers/steelseries/aerox3-hid.d.ts.map +1 -0
- package/dist/drivers/steelseries/aerox3-hid.js +172 -0
- package/dist/drivers/steelseries/aerox3-hid.js.map +1 -0
- package/dist/drivers/steelseries/aerox5-hid.d.ts +54 -0
- package/dist/drivers/steelseries/aerox5-hid.d.ts.map +1 -0
- package/dist/drivers/steelseries/aerox5-hid.js +173 -0
- package/dist/drivers/steelseries/aerox5-hid.js.map +1 -0
- package/dist/drivers/steelseries/aerox5-wireless-hid.d.ts +69 -0
- package/dist/drivers/steelseries/aerox5-wireless-hid.d.ts.map +1 -0
- package/dist/drivers/steelseries/aerox5-wireless-hid.js +234 -0
- package/dist/drivers/steelseries/aerox5-wireless-hid.js.map +1 -0
- package/dist/drivers/steelseries/aerox9-wireless-hid.d.ts +70 -0
- package/dist/drivers/steelseries/aerox9-wireless-hid.d.ts.map +1 -0
- package/dist/drivers/steelseries/aerox9-wireless-hid.js +227 -0
- package/dist/drivers/steelseries/aerox9-wireless-hid.js.map +1 -0
- package/dist/drivers/steelseries/hid.d.ts +48 -0
- package/dist/drivers/steelseries/hid.d.ts.map +1 -0
- package/dist/drivers/steelseries/hid.js +157 -0
- package/dist/drivers/steelseries/hid.js.map +1 -0
- package/dist/drivers/steelseries/prime-mini-wireless-hid.d.ts +62 -0
- package/dist/drivers/steelseries/prime-mini-wireless-hid.d.ts.map +1 -0
- package/dist/drivers/steelseries/prime-mini-wireless-hid.js +215 -0
- package/dist/drivers/steelseries/prime-mini-wireless-hid.js.map +1 -0
- package/dist/drivers/steelseries/prime-plus-hid.d.ts +51 -0
- package/dist/drivers/steelseries/prime-plus-hid.d.ts.map +1 -0
- package/dist/drivers/steelseries/prime-plus-hid.js +150 -0
- package/dist/drivers/steelseries/prime-plus-hid.js.map +1 -0
- package/dist/drivers/steelseries/rival3-wireless-hid.d.ts +61 -0
- package/dist/drivers/steelseries/rival3-wireless-hid.d.ts.map +1 -0
- package/dist/drivers/steelseries/rival3-wireless-hid.js +185 -0
- package/dist/drivers/steelseries/rival3-wireless-hid.js.map +1 -0
- package/dist/drivers/steelseries/rival310-hid.d.ts +59 -0
- package/dist/drivers/steelseries/rival310-hid.d.ts.map +1 -0
- package/dist/drivers/steelseries/rival310-hid.js +182 -0
- package/dist/drivers/steelseries/rival310-hid.js.map +1 -0
- package/dist/drivers/steelseries/rival650-hid.d.ts +68 -0
- package/dist/drivers/steelseries/rival650-hid.d.ts.map +1 -0
- package/dist/drivers/steelseries/rival650-hid.js +209 -0
- package/dist/drivers/steelseries/rival650-hid.js.map +1 -0
- package/dist/drivers/steelseries/sensei-ten-hid.d.ts +62 -0
- package/dist/drivers/steelseries/sensei-ten-hid.d.ts.map +1 -0
- package/dist/drivers/steelseries/sensei-ten-hid.js +186 -0
- package/dist/drivers/steelseries/sensei-ten-hid.js.map +1 -0
- package/dist/drivers/teevolution/hid.d.ts +82 -0
- package/dist/drivers/teevolution/hid.d.ts.map +1 -0
- package/dist/drivers/teevolution/hid.js +607 -0
- package/dist/drivers/teevolution/hid.js.map +1 -0
- package/dist/drivers/vendors.d.ts +254 -0
- package/dist/drivers/vendors.d.ts.map +1 -0
- package/dist/drivers/vendors.js +587 -0
- package/dist/drivers/vendors.js.map +1 -0
- package/dist/drivers/vgn/hid.d.ts +33 -0
- package/dist/drivers/vgn/hid.d.ts.map +1 -0
- package/dist/drivers/vgn/hid.js +213 -0
- package/dist/drivers/vgn/hid.js.map +1 -0
- package/dist/drivers/wallhack/keyboard-hid.d.ts +28 -0
- package/dist/drivers/wallhack/keyboard-hid.d.ts.map +1 -0
- package/dist/drivers/wallhack/keyboard-hid.js +80 -0
- package/dist/drivers/wallhack/keyboard-hid.js.map +1 -0
- package/dist/drivers/wallhack/mouse-hid.d.ts +45 -0
- package/dist/drivers/wallhack/mouse-hid.d.ts.map +1 -0
- package/dist/drivers/wallhack/mouse-hid.js +288 -0
- package/dist/drivers/wallhack/mouse-hid.js.map +1 -0
- package/dist/drivers/webhid.d.ts +59 -0
- package/dist/drivers/webhid.d.ts.map +1 -0
- package/dist/drivers/webhid.js +2 -0
- package/dist/drivers/webhid.js.map +1 -0
- package/dist/drivers/wlmouse/hid.d.ts +81 -0
- package/dist/drivers/wlmouse/hid.d.ts.map +1 -0
- package/dist/drivers/wlmouse/hid.js +576 -0
- package/dist/drivers/wlmouse/hid.js.map +1 -0
- package/dist/drivers/wooting/hid.d.ts +86 -0
- package/dist/drivers/wooting/hid.d.ts.map +1 -0
- package/dist/drivers/wooting/hid.js +320 -0
- package/dist/drivers/wooting/hid.js.map +1 -0
- package/dist/drivers/zaunkoenig/hid.d.ts +31 -0
- package/dist/drivers/zaunkoenig/hid.d.ts.map +1 -0
- package/dist/drivers/zaunkoenig/hid.js +162 -0
- package/dist/drivers/zaunkoenig/hid.js.map +1 -0
- package/dist/endgame-gear/op1.d.ts +165 -0
- package/dist/endgame-gear/op1.d.ts.map +1 -0
- package/dist/endgame-gear/op1.js +384 -0
- package/dist/endgame-gear/op1.js.map +1 -0
- package/dist/endgame-gear/wireless.d.ts +93 -0
- package/dist/endgame-gear/wireless.d.ts.map +1 -0
- package/dist/endgame-gear/wireless.js +242 -0
- package/dist/endgame-gear/wireless.js.map +1 -0
- package/dist/fantech/index.d.ts +9 -0
- package/dist/fantech/index.d.ts.map +1 -0
- package/dist/fantech/index.js +11 -0
- package/dist/fantech/index.js.map +1 -0
- package/dist/finalmouse/index.d.ts +41 -0
- package/dist/finalmouse/index.d.ts.map +1 -0
- package/dist/finalmouse/index.js +106 -0
- package/dist/finalmouse/index.js.map +1 -0
- package/dist/gearhub/index.d.ts +210 -0
- package/dist/gearhub/index.d.ts.map +1 -0
- package/dist/gearhub/index.js +282 -0
- package/dist/gearhub/index.js.map +1 -0
- package/dist/glorious/index.d.ts +89 -0
- package/dist/glorious/index.d.ts.map +1 -0
- package/dist/glorious/index.js +236 -0
- package/dist/glorious/index.js.map +1 -0
- package/dist/glorious-classic/index.d.ts +103 -0
- package/dist/glorious-classic/index.d.ts.map +1 -0
- package/dist/glorious-classic/index.js +304 -0
- package/dist/glorious-classic/index.js.map +1 -0
- package/dist/gwolves/index.d.ts +82 -0
- package/dist/gwolves/index.d.ts.map +1 -0
- package/dist/gwolves/index.js +197 -0
- package/dist/gwolves/index.js.map +1 -0
- package/dist/hyperx/index.d.ts +181 -0
- package/dist/hyperx/index.d.ts.map +1 -0
- package/dist/hyperx/index.js +303 -0
- package/dist/hyperx/index.js.map +1 -0
- package/dist/incott/index.d.ts +1067 -0
- package/dist/incott/index.d.ts.map +1 -0
- package/dist/incott/index.js +1482 -0
- package/dist/incott/index.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/keychron/index.d.ts +194 -0
- package/dist/keychron/index.d.ts.map +1 -0
- package/dist/keychron/index.js +341 -0
- package/dist/keychron/index.js.map +1 -0
- package/dist/ksnake/index.d.ts +157 -0
- package/dist/ksnake/index.d.ts.map +1 -0
- package/dist/ksnake/index.js +314 -0
- package/dist/ksnake/index.js.map +1 -0
- package/dist/lamzu/atlantis.d.ts +237 -0
- package/dist/lamzu/atlantis.d.ts.map +1 -0
- package/dist/lamzu/atlantis.js +263 -0
- package/dist/lamzu/atlantis.js.map +1 -0
- package/dist/lamzu/index.d.ts +60 -0
- package/dist/lamzu/index.d.ts.map +1 -0
- package/dist/lamzu/index.js +86 -0
- package/dist/lamzu/index.js.map +1 -0
- package/dist/logitech/controls.d.ts +138 -0
- package/dist/logitech/controls.d.ts.map +1 -0
- package/dist/logitech/controls.js +184 -0
- package/dist/logitech/controls.js.map +1 -0
- package/dist/logitech/friendly-name.d.ts +50 -0
- package/dist/logitech/friendly-name.d.ts.map +1 -0
- package/dist/logitech/friendly-name.js +74 -0
- package/dist/logitech/friendly-name.js.map +1 -0
- package/dist/logitech/haptics.d.ts +97 -0
- package/dist/logitech/haptics.d.ts.map +1 -0
- package/dist/logitech/haptics.js +116 -0
- package/dist/logitech/haptics.js.map +1 -0
- package/dist/logitech/hosts.d.ts +55 -0
- package/dist/logitech/hosts.d.ts.map +1 -0
- package/dist/logitech/hosts.js +80 -0
- package/dist/logitech/hosts.js.map +1 -0
- package/dist/logitech/index.d.ts +127 -0
- package/dist/logitech/index.d.ts.map +1 -0
- package/dist/logitech/index.js +233 -0
- package/dist/logitech/index.js.map +1 -0
- package/dist/logitech/wheel.d.ts +117 -0
- package/dist/logitech/wheel.d.ts.map +1 -0
- package/dist/logitech/wheel.js +133 -0
- package/dist/logitech/wheel.js.map +1 -0
- package/dist/mchose/a5-gen1.d.ts +38 -0
- package/dist/mchose/a5-gen1.d.ts.map +1 -0
- package/dist/mchose/a5-gen1.js +60 -0
- package/dist/mchose/a5-gen1.js.map +1 -0
- package/dist/mchose/buttons.d.ts +66 -0
- package/dist/mchose/buttons.d.ts.map +1 -0
- package/dist/mchose/buttons.js +184 -0
- package/dist/mchose/buttons.js.map +1 -0
- package/dist/mchose/dock.d.ts +77 -0
- package/dist/mchose/dock.d.ts.map +1 -0
- package/dist/mchose/dock.js +133 -0
- package/dist/mchose/dock.js.map +1 -0
- package/dist/mchose/index.d.ts +299 -0
- package/dist/mchose/index.d.ts.map +1 -0
- package/dist/mchose/index.js +429 -0
- package/dist/mchose/index.js.map +1 -0
- package/dist/mchose/v3-buttons.d.ts +64 -0
- package/dist/mchose/v3-buttons.d.ts.map +1 -0
- package/dist/mchose/v3-buttons.js +258 -0
- package/dist/mchose/v3-buttons.js.map +1 -0
- package/dist/mchose/v3.d.ts +380 -0
- package/dist/mchose/v3.d.ts.map +1 -0
- package/dist/mchose/v3.js +608 -0
- package/dist/mchose/v3.js.map +1 -0
- package/dist/microsoft/index.d.ts +19 -0
- package/dist/microsoft/index.d.ts.map +1 -0
- package/dist/microsoft/index.js +22 -0
- package/dist/microsoft/index.js.map +1 -0
- package/dist/moddo/index.d.ts +72 -0
- package/dist/moddo/index.d.ts.map +1 -0
- package/dist/moddo/index.js +147 -0
- package/dist/moddo/index.js.map +1 -0
- package/dist/ninjutso/index.d.ts +74 -0
- package/dist/ninjutso/index.d.ts.map +1 -0
- package/dist/ninjutso/index.js +133 -0
- package/dist/ninjutso/index.js.map +1 -0
- package/dist/orbital/index.d.ts +14 -0
- package/dist/orbital/index.d.ts.map +1 -0
- package/dist/orbital/index.js +19 -0
- package/dist/orbital/index.js.map +1 -0
- package/dist/pulsar/index.d.ts +81 -0
- package/dist/pulsar/index.d.ts.map +1 -0
- package/dist/pulsar/index.js +185 -0
- package/dist/pulsar/index.js.map +1 -0
- package/dist/rawm/index.d.ts +55 -0
- package/dist/rawm/index.d.ts.map +1 -0
- package/dist/rawm/index.js +229 -0
- package/dist/rawm/index.js.map +1 -0
- package/dist/razer/codec.d.ts +504 -0
- package/dist/razer/codec.d.ts.map +1 -0
- package/dist/razer/codec.js +648 -0
- package/dist/razer/codec.js.map +1 -0
- package/dist/razer/devices.d.ts +156 -0
- package/dist/razer/devices.d.ts.map +1 -0
- package/dist/razer/devices.js +540 -0
- package/dist/razer/devices.js.map +1 -0
- package/dist/razer/index.d.ts +2 -0
- package/dist/razer/index.d.ts.map +1 -0
- package/dist/razer/index.js +2 -0
- package/dist/razer/index.js.map +1 -0
- package/dist/razer/v4.d.ts +19 -0
- package/dist/razer/v4.d.ts.map +1 -0
- package/dist/razer/v4.js +51 -0
- package/dist/razer/v4.js.map +1 -0
- package/dist/steelseries/aerox3.d.ts +236 -0
- package/dist/steelseries/aerox3.d.ts.map +1 -0
- package/dist/steelseries/aerox3.js +311 -0
- package/dist/steelseries/aerox3.js.map +1 -0
- package/dist/steelseries/aerox5-wireless.d.ts +300 -0
- package/dist/steelseries/aerox5-wireless.d.ts.map +1 -0
- package/dist/steelseries/aerox5-wireless.js +389 -0
- package/dist/steelseries/aerox5-wireless.js.map +1 -0
- package/dist/steelseries/aerox5.d.ts +228 -0
- package/dist/steelseries/aerox5.d.ts.map +1 -0
- package/dist/steelseries/aerox5.js +294 -0
- package/dist/steelseries/aerox5.js.map +1 -0
- package/dist/steelseries/aerox9-wireless.d.ts +213 -0
- package/dist/steelseries/aerox9-wireless.d.ts.map +1 -0
- package/dist/steelseries/aerox9-wireless.js +305 -0
- package/dist/steelseries/aerox9-wireless.js.map +1 -0
- package/dist/steelseries/devices.d.ts +82 -0
- package/dist/steelseries/devices.d.ts.map +1 -0
- package/dist/steelseries/devices.js +310 -0
- package/dist/steelseries/devices.js.map +1 -0
- package/dist/steelseries/index.d.ts +13 -0
- package/dist/steelseries/index.d.ts.map +1 -0
- package/dist/steelseries/index.js +13 -0
- package/dist/steelseries/index.js.map +1 -0
- package/dist/steelseries/prime-mini-wireless.d.ts +253 -0
- package/dist/steelseries/prime-mini-wireless.d.ts.map +1 -0
- package/dist/steelseries/prime-mini-wireless.js +351 -0
- package/dist/steelseries/prime-mini-wireless.js.map +1 -0
- package/dist/steelseries/prime-plus.d.ts +179 -0
- package/dist/steelseries/prime-plus.d.ts.map +1 -0
- package/dist/steelseries/prime-plus.js +275 -0
- package/dist/steelseries/prime-plus.js.map +1 -0
- package/dist/steelseries/rival3-wireless.d.ts +222 -0
- package/dist/steelseries/rival3-wireless.d.ts.map +1 -0
- package/dist/steelseries/rival3-wireless.js +308 -0
- package/dist/steelseries/rival3-wireless.js.map +1 -0
- package/dist/steelseries/rival3.d.ts +99 -0
- package/dist/steelseries/rival3.d.ts.map +1 -0
- package/dist/steelseries/rival3.js +147 -0
- package/dist/steelseries/rival3.js.map +1 -0
- package/dist/steelseries/rival310.d.ts +230 -0
- package/dist/steelseries/rival310.d.ts.map +1 -0
- package/dist/steelseries/rival310.js +322 -0
- package/dist/steelseries/rival310.js.map +1 -0
- package/dist/steelseries/rival650.d.ts +215 -0
- package/dist/steelseries/rival650.d.ts.map +1 -0
- package/dist/steelseries/rival650.js +284 -0
- package/dist/steelseries/rival650.js.map +1 -0
- package/dist/steelseries/sensei-ten.d.ts +283 -0
- package/dist/steelseries/sensei-ten.d.ts.map +1 -0
- package/dist/steelseries/sensei-ten.js +389 -0
- package/dist/steelseries/sensei-ten.js.map +1 -0
- package/dist/teevolution/index.d.ts +211 -0
- package/dist/teevolution/index.d.ts.map +1 -0
- package/dist/teevolution/index.js +458 -0
- package/dist/teevolution/index.js.map +1 -0
- package/dist/valkyrie/index.d.ts +19 -0
- package/dist/valkyrie/index.d.ts.map +1 -0
- package/dist/valkyrie/index.js +43 -0
- package/dist/valkyrie/index.js.map +1 -0
- package/dist/valkyrie/settings.d.ts +28 -0
- package/dist/valkyrie/settings.d.ts.map +1 -0
- package/dist/valkyrie/settings.js +84 -0
- package/dist/valkyrie/settings.js.map +1 -0
- package/dist/vgn/index.d.ts +59 -0
- package/dist/vgn/index.d.ts.map +1 -0
- package/dist/vgn/index.js +165 -0
- package/dist/vgn/index.js.map +1 -0
- package/dist/wallhack/index.d.ts +165 -0
- package/dist/wallhack/index.d.ts.map +1 -0
- package/dist/wallhack/index.js +226 -0
- package/dist/wallhack/index.js.map +1 -0
- package/dist/wlmouse/index.d.ts +4 -0
- package/dist/wlmouse/index.d.ts.map +1 -0
- package/dist/wlmouse/index.js +7 -0
- package/dist/wlmouse/index.js.map +1 -0
- package/dist/wooting/index.d.ts +225 -0
- package/dist/wooting/index.d.ts.map +1 -0
- package/dist/wooting/index.js +296 -0
- package/dist/wooting/index.js.map +1 -0
- package/dist/zaunkoenig/index.d.ts +49 -0
- package/dist/zaunkoenig/index.d.ts.map +1 -0
- package/dist/zaunkoenig/index.js +127 -0
- package/dist/zaunkoenig/index.js.map +1 -0
- package/package.json +193 -0
|
@@ -0,0 +1,772 @@
|
|
|
1
|
+
import { VENDOR_ID } from "../vendors.js";
|
|
2
|
+
import { RATES_1K, RATES_8K, RAZER_PRODUCTS } from "@openmouse/protocol/razer-devices";
|
|
3
|
+
import { openRazerDevice } from "./hid-open.js";
|
|
4
|
+
import { RAZER_BUTTON_CONTROLS, RAZER_BUTTON_CONTROL_LABEL, RAZER_LANDING_MAX, RAZER_LANDING_MIN, RAZER_LIFT_OFF_MAX, RAZER_LIFT_OFF_MIN, RAZER_READ, RAZER_TOGGLE_CONTROLS, RAZER_TOGGLE_CONTROL_INFO, RAZER_REPORT_ID, RAZER_STORAGE, RAZER_STATUS, RAZER_TRACKING_DISTANCES, RAZER_TRANSACTION_ID, RazerProtocolError, decodeBatteryPercent, decodeCharging, decodeDpi, decodeExtendedPollingRate, decodeFirmwareVersion, decodeLegacyPollingRate, decodeLiftOff, decodeLowPowerThreshold, decodeRazerResponse, decodeSerial, decodeSleepTimeout, encodeRazerRequest, isRazerGetter, razerDecodeButtonMapping, razerDecodeToggleControl, razerReadButtonMappingCommand, razerReadToggleControlCommand, razerSetButtonMappingCommand, razerSetToggleControlCommand, razerReadDpiCommand, razerSetDpiCommand, razerSetExtendedPollingCommand, razerSetLegacyPollingCommand, razerMaxLanding, razerSetLiftOffCommand, razerSetTrackingDistanceCommand, razerEnableAsymmetricLiftOffCommand, razerEnableAsymmetricLiftOffCanonicalCommand, razerEnableSensorCalibrationCommand, razerSetLowPowerThresholdCommand, razerSetSleepTimeoutCommand, } from "@openmouse/protocol/razer";
|
|
5
|
+
// The sensor takes any whole DPI from here up to the model's ceiling, per axis.
|
|
6
|
+
const DPI_MIN = 100;
|
|
7
|
+
const RESPONSE_DELAY_MS = 100;
|
|
8
|
+
const RESPONSE_ATTEMPTS = 6;
|
|
9
|
+
// How long a polling-rate change takes to re-establish the wireless link. The
|
|
10
|
+
// Viper V4 Pro driver pauses 150 ms for the same reason.
|
|
11
|
+
const POLLING_LINK_SETTLE_MS = 150;
|
|
12
|
+
// The vendor software slides from 1 to 15 minutes, so those are the bounds this
|
|
13
|
+
// model is meant to hold. The firmware itself accepts less — 30 s round-tripped
|
|
14
|
+
// exactly, below even the 60 s floor OpenRazer documents — but nothing offers
|
|
15
|
+
// that, so it is not offered here either.
|
|
16
|
+
const SLEEP_MIN_SECONDS = 60;
|
|
17
|
+
const SLEEP_MAX_SECONDS = 900;
|
|
18
|
+
const SLEEP_STEP_SECONDS = 60;
|
|
19
|
+
// One entry per minute, matching the vendor slider exactly rather than sampling
|
|
20
|
+
// it, so no offered value is an interpolation.
|
|
21
|
+
const SLEEP_OPTIONS = Array.from({ length: SLEEP_MAX_SECONDS / SLEEP_STEP_SECONDS }, (_, index) => (index + 1) * SLEEP_STEP_SECONDS);
|
|
22
|
+
// Synapse slides this from 5 to 100 percent. The value is stored on the battery
|
|
23
|
+
// reads' 0–255 scale, so the offered percentages are what round-trip cleanly
|
|
24
|
+
// through it rather than every whole percent.
|
|
25
|
+
const LOW_POWER_MIN_PERCENT = 5;
|
|
26
|
+
const LOW_POWER_MAX_PERCENT = 100;
|
|
27
|
+
const LOW_POWER_STEP_PERCENT = 5;
|
|
28
|
+
const LOW_POWER_OPTIONS = Array.from({ length: (LOW_POWER_MAX_PERCENT - LOW_POWER_MIN_PERCENT) / LOW_POWER_STEP_PERCENT + 1 }, (_, index) => LOW_POWER_MIN_PERCENT + index * LOW_POWER_STEP_PERCENT);
|
|
29
|
+
// Synapse states low power mode is unavailable at 2000 Hz and above, and greys
|
|
30
|
+
// the slider out there. The setting still reads, so this bounds the control
|
|
31
|
+
// rather than the command.
|
|
32
|
+
const LOW_POWER_MAX_POLLING_HZ = 1000;
|
|
33
|
+
/**
|
|
34
|
+
* Razer exposes its control channel on the interface whose only collection is
|
|
35
|
+
* Generic Desktop Mouse. Every other interface either belongs to a different
|
|
36
|
+
* function or is a protected collection the browser will not talk to.
|
|
37
|
+
*/
|
|
38
|
+
function isMouseControlInterface(device) {
|
|
39
|
+
const [collection, ...rest] = device.collections;
|
|
40
|
+
return rest.length === 0 && collection?.usagePage === 0x01 && collection?.usage === 0x02;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Older Razer mice carry the configuration channel on a vendor-defined
|
|
44
|
+
* interface instead, and which one varies by hardware revision. Accepting both
|
|
45
|
+
* shapes means the picker can offer either without the driver refusing it.
|
|
46
|
+
*/
|
|
47
|
+
function hasVendorCollection(device) {
|
|
48
|
+
return device.collections.some((collection) => (collection.usagePage ?? 0) >= 0xff00);
|
|
49
|
+
}
|
|
50
|
+
export class RazerHidClient {
|
|
51
|
+
queue = Promise.resolve();
|
|
52
|
+
staticReads = new Map();
|
|
53
|
+
// The mode probe is a write, so it runs once per connection rather than on
|
|
54
|
+
// every background refresh. Both setters know which mode they leave behind,
|
|
55
|
+
// so nothing after the first read needs to ask the mouse again.
|
|
56
|
+
asymmetric = null;
|
|
57
|
+
asymmetricKnown = false;
|
|
58
|
+
// Same reasoning as asymmetric/asymmetricKnown: read once per connection
|
|
59
|
+
// rather than on every background refresh. Button-mapping writes use their
|
|
60
|
+
// own transaction id (RAZER_BUTTON_TRANSACTION_ID) and are confirmed by an
|
|
61
|
+
// immediate read-back in setButtonMapping — polling this command class on a
|
|
62
|
+
// timer would put an unrelated read between a write and its own verify-read
|
|
63
|
+
// for no benefit, since nothing changes these but setButtonMapping itself.
|
|
64
|
+
buttonMappings = null;
|
|
65
|
+
buttonMappingsKnown = false;
|
|
66
|
+
/** Filled for dock passthrough after the first polling probe. */
|
|
67
|
+
discoveredPollingRates = null;
|
|
68
|
+
/** Which polling command the paired mouse answers; null until probed. */
|
|
69
|
+
discoveredHighRatePolling = null;
|
|
70
|
+
device;
|
|
71
|
+
constructor(device) {
|
|
72
|
+
this.device = device;
|
|
73
|
+
}
|
|
74
|
+
static isSupported(device) {
|
|
75
|
+
const product = RAZER_PRODUCTS.get(device.productId);
|
|
76
|
+
if (device.vendorId !== VENDOR_ID.razer || !product)
|
|
77
|
+
return false;
|
|
78
|
+
// A nativeOnly model's control channel sits on a collection the browser
|
|
79
|
+
// refuses to expose, so the WebHID driver must never claim it even when a
|
|
80
|
+
// previously granted device enumerates (auto-reconnect, shared grants).
|
|
81
|
+
if (product.nativeOnly)
|
|
82
|
+
return false;
|
|
83
|
+
return isMouseControlInterface(device)
|
|
84
|
+
|| (product.vendorControlInterface === true && hasVendorCollection(device));
|
|
85
|
+
}
|
|
86
|
+
profile() {
|
|
87
|
+
return RAZER_PRODUCTS.get(this.device.productId);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Whether polling uses the extended command (a divisor of 8000) rather than
|
|
91
|
+
* the legacy one (a divisor of 1000).
|
|
92
|
+
*
|
|
93
|
+
* Asked of the product rather than of the connection: the Viper 8KHz is wired
|
|
94
|
+
* and needs the extended command, while the pre-HyperPolling receivers are
|
|
95
|
+
* wireless and answer only the legacy one. For every model verified so far
|
|
96
|
+
* this is exactly `isWireless()`, which is what it used to read.
|
|
97
|
+
*/
|
|
98
|
+
usesHighRatePolling() {
|
|
99
|
+
if (this.discoveredHighRatePolling !== null)
|
|
100
|
+
return this.discoveredHighRatePolling;
|
|
101
|
+
return this.profile()?.highRatePolling ?? this.isWireless();
|
|
102
|
+
}
|
|
103
|
+
/** DPI store byte; most models store, the V2 generation does not. */
|
|
104
|
+
dpiStorageByte() {
|
|
105
|
+
return this.profile()?.dpiStorageByte ?? RAZER_STORAGE;
|
|
106
|
+
}
|
|
107
|
+
async open() {
|
|
108
|
+
await openRazerDevice(this.device);
|
|
109
|
+
}
|
|
110
|
+
async close() {
|
|
111
|
+
this.staticReads.clear();
|
|
112
|
+
this.asymmetricKnown = false;
|
|
113
|
+
this.buttonMappingsKnown = false;
|
|
114
|
+
this.discoveredPollingRates = null;
|
|
115
|
+
this.discoveredHighRatePolling = null;
|
|
116
|
+
if (this.device.opened)
|
|
117
|
+
await this.device.close();
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Whether this collection actually answers the Razer protocol, tried by
|
|
121
|
+
* sending the firmware-version read rather than inferred from the
|
|
122
|
+
* collection's declared shape. `isSupported()`'s shape check (single
|
|
123
|
+
* collection, Generic Desktop Mouse) is right for every model verified so
|
|
124
|
+
* far, but WebHID does not validate report ids against the descriptor (see
|
|
125
|
+
* the note atop codec.ts), so a collection that fails the shape check can
|
|
126
|
+
* still genuinely answer report 0 — used as a fallback in
|
|
127
|
+
* `razerProbeControlInterface` for exactly that case, not as the normal
|
|
128
|
+
* detection path.
|
|
129
|
+
*/
|
|
130
|
+
async probeReachable() {
|
|
131
|
+
try {
|
|
132
|
+
await this.request(RAZER_READ.firmware);
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
displayName() {
|
|
140
|
+
const known = this.profile();
|
|
141
|
+
return known ? `Razer ${known.model}` : this.device.productName || "Razer";
|
|
142
|
+
}
|
|
143
|
+
isWireless() {
|
|
144
|
+
return this.profile()?.wireless ?? false;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Says so when the model has never been connected by this project.
|
|
148
|
+
*
|
|
149
|
+
* The commands are the ones already confirmed elsewhere, but which of them a
|
|
150
|
+
* given model answers is transcribed rather than measured, so the panel
|
|
151
|
+
* should not present it as the same thing as a tested mouse.
|
|
152
|
+
*/
|
|
153
|
+
connectionDetail(wireless) {
|
|
154
|
+
const link = this.profile()?.connectionLabel
|
|
155
|
+
?? (wireless ? "HyperSpeed receiver" : "Wired USB");
|
|
156
|
+
return this.profile()?.verified === false ? `${link} · untested model` : link;
|
|
157
|
+
}
|
|
158
|
+
maxDpi() {
|
|
159
|
+
return this.profile()?.maxDpi ?? 35000;
|
|
160
|
+
}
|
|
161
|
+
getSupportedPollingRates() {
|
|
162
|
+
if (this.discoveredPollingRates)
|
|
163
|
+
return [...this.discoveredPollingRates];
|
|
164
|
+
return [...(this.profile()?.pollingRates ?? RATES_1K)];
|
|
165
|
+
}
|
|
166
|
+
/** Seconds, matching what the panel labels and what `setSleepTimeout` takes. */
|
|
167
|
+
getSleepOptions() {
|
|
168
|
+
return [...SLEEP_OPTIONS];
|
|
169
|
+
}
|
|
170
|
+
/** Whole percentages, matching the vendor slider. */
|
|
171
|
+
getLowPowerOptions() {
|
|
172
|
+
return [...LOW_POWER_OPTIONS];
|
|
173
|
+
}
|
|
174
|
+
/** The rate above which the vendor software refuses to arm low power mode. */
|
|
175
|
+
getLowPowerPollingCeiling() {
|
|
176
|
+
return LOW_POWER_MAX_POLLING_HZ;
|
|
177
|
+
}
|
|
178
|
+
/** Every whole value, because the control validates entries against this list. */
|
|
179
|
+
getDpiOptions() {
|
|
180
|
+
const options = [];
|
|
181
|
+
for (let dpi = DPI_MIN; dpi <= this.maxDpi(); dpi += 1)
|
|
182
|
+
options.push(dpi);
|
|
183
|
+
return options;
|
|
184
|
+
}
|
|
185
|
+
async readStatus() {
|
|
186
|
+
await this.open();
|
|
187
|
+
const wireless = this.isWireless();
|
|
188
|
+
const firmware = await this.once("firmware", () => this.request(RAZER_READ.firmware));
|
|
189
|
+
if (!firmware)
|
|
190
|
+
throw new Error("The mouse did not report a firmware version.");
|
|
191
|
+
const serial = await this.once("serial", () => this.request(RAZER_READ.serial).catch(() => null));
|
|
192
|
+
// A wired mouse with no cell answers battery and power-management commands
|
|
193
|
+
// as unsupported, which would otherwise abort the whole status read.
|
|
194
|
+
const hasBattery = this.profile()?.hasBattery !== false;
|
|
195
|
+
// On a model nobody has connected, `hasBattery` is a prediction. An
|
|
196
|
+
// unsupported reply throws, and this read is not optional the way sleep and
|
|
197
|
+
// low power are, so it would abort the whole status read and take DPI and
|
|
198
|
+
// polling down with it rather than dropping one card. Verified models still
|
|
199
|
+
// fail loudly: there, a battery command that stopped answering is news.
|
|
200
|
+
const battery = hasBattery
|
|
201
|
+
? await this.readBattery().catch((error) => {
|
|
202
|
+
if (this.profile()?.verified === true)
|
|
203
|
+
throw error;
|
|
204
|
+
return null;
|
|
205
|
+
})
|
|
206
|
+
: null;
|
|
207
|
+
// Both transports answer this, unlike polling. A transport that ever stops
|
|
208
|
+
// reports no timeout and hides the control rather than failing the whole
|
|
209
|
+
// read, which would take DPI and battery down with it.
|
|
210
|
+
const sleep = hasBattery ? await this.request(RAZER_READ.sleepTimeout).catch(() => null) : null;
|
|
211
|
+
const lowPower = hasBattery ? await this.request(RAZER_READ.lowPowerThreshold).catch(() => null) : null;
|
|
212
|
+
const dpi = decodeDpi(await this.request(razerReadDpiCommand(this.dpiStorageByte())));
|
|
213
|
+
const pollingRateHz = await this.readPollingRateHz();
|
|
214
|
+
const buttonMappings = await this.currentButtonMappings();
|
|
215
|
+
// Asked of the product, not of the mouse. A model without lift-off can
|
|
216
|
+
// still answer this read — the Basilisk X HyperSpeed returns status 0x02
|
|
217
|
+
// with an all-zero payload, which decodes as a perfectly ordinary "Low" —
|
|
218
|
+
// so a successful reply proves nothing and the control cannot be offered on
|
|
219
|
+
// the strength of one. Skipping it also spares 0x0b a round trip on every
|
|
220
|
+
// background refresh for every model that does not have it.
|
|
221
|
+
const liftOff = this.profile()?.liftOff === true ? await this.readLiftOff() : null;
|
|
222
|
+
return {
|
|
223
|
+
brand: "Razer",
|
|
224
|
+
name: this.displayName(),
|
|
225
|
+
ui: {
|
|
226
|
+
family: "razer",
|
|
227
|
+
settingsReady: true,
|
|
228
|
+
valuesVerified: true,
|
|
229
|
+
hideUnsupportedPollingRates: true,
|
|
230
|
+
// No sensor-processing command is confirmed, so that card is hidden
|
|
231
|
+
// rather than offered and left inert. Lift-off is confirmed and
|
|
232
|
+
// readable through `readLiftOff`, but `MouseStatus` cannot carry the
|
|
233
|
+
// asymmetric pair yet — see the note on `liftOffDistance` below.
|
|
234
|
+
hideProcessingCard: true,
|
|
235
|
+
// Nothing reports link quality on this model, and the section this card
|
|
236
|
+
// shares with sleep is opened below, so it would otherwise appear as a
|
|
237
|
+
// permanent "signal is unavailable" placeholder.
|
|
238
|
+
hideSignalCard: true,
|
|
239
|
+
// Auto sleep and button mappings are what this driver puts in that
|
|
240
|
+
// section, so it opens only when at least one of them answered.
|
|
241
|
+
showAdvancedSection: sleep !== null || buttonMappings !== null,
|
|
242
|
+
forceShowBattery: battery ? true : undefined,
|
|
243
|
+
defaultDisplayName: this.profile()?.model,
|
|
244
|
+
},
|
|
245
|
+
batteryPercent: battery?.percent ?? null,
|
|
246
|
+
batteryState: battery?.state ?? "Unknown",
|
|
247
|
+
dpi: dpi.x,
|
|
248
|
+
dpiY: dpi.y,
|
|
249
|
+
supportsSeparateDpiAxes: true,
|
|
250
|
+
pollingRateHz,
|
|
251
|
+
supportedPollingRates: this.getSupportedPollingRates(),
|
|
252
|
+
activeProfile: null,
|
|
253
|
+
connectionType: wireless ? "Wireless" : "Wired",
|
|
254
|
+
connectionDetail: this.connectionDetail(wireless),
|
|
255
|
+
sleepTimeout: sleep ? decodeSleepTimeout(sleep) : null,
|
|
256
|
+
lowBatteryWarning: lowPower ? decodeLowPowerThreshold(lowPower) : null,
|
|
257
|
+
unitId: serial ? decodeSerial(serial) : null,
|
|
258
|
+
liftOffDistance: liftOff?.tracking ?? null,
|
|
259
|
+
// An empty list hides the control, which is what should happen on a
|
|
260
|
+
// transport that does not answer class 0x0b at all.
|
|
261
|
+
supportedLiftOffDistances: liftOff ? [...RAZER_TRACKING_DISTANCES] : [],
|
|
262
|
+
asymmetricLiftOff: liftOff && this.hasAsymmetricLiftOff()
|
|
263
|
+
? {
|
|
264
|
+
enabled: await this.asymmetricMode(liftOff),
|
|
265
|
+
liftOff: liftOff.liftOff,
|
|
266
|
+
landing: liftOff.landing,
|
|
267
|
+
liftOffRange: { min: RAZER_LIFT_OFF_MIN, max: RAZER_LIFT_OFF_MAX },
|
|
268
|
+
landingRange: { min: RAZER_LANDING_MIN, max: RAZER_LANDING_MAX },
|
|
269
|
+
}
|
|
270
|
+
: null,
|
|
271
|
+
razerButtonMappings: buttonMappings ?? undefined,
|
|
272
|
+
firmware: [`Mouse ${decodeFirmwareVersion(firmware)}`],
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Every shipped control's current state, keyed by control name — the four
|
|
277
|
+
* cross-assignable `RazerButtonControl`s and the three two-state
|
|
278
|
+
* `RazerToggleControl`s share one dict, since the view layer reads each
|
|
279
|
+
* family by iterating its own fixed control list (`RAZER_BUTTON_CONTROLS` /
|
|
280
|
+
* `RAZER_TOGGLE_CONTROLS`), and the two lists share no control names. Null
|
|
281
|
+
* on a model that has not been confirmed to support this write at all. A
|
|
282
|
+
* control whose reply this driver cannot decode is omitted rather than
|
|
283
|
+
* guessed at.
|
|
284
|
+
*/
|
|
285
|
+
async readButtonMappings() {
|
|
286
|
+
if (this.profile()?.buttonMapping !== true)
|
|
287
|
+
return null;
|
|
288
|
+
const mappings = {};
|
|
289
|
+
await Promise.all([
|
|
290
|
+
...RAZER_BUTTON_CONTROLS.map(async (control) => {
|
|
291
|
+
const reply = await this.request(razerReadButtonMappingCommand(control)).catch(() => null);
|
|
292
|
+
const mapping = reply ? razerDecodeButtonMapping(reply) : null;
|
|
293
|
+
if (mapping)
|
|
294
|
+
mappings[control] = mapping;
|
|
295
|
+
}),
|
|
296
|
+
...RAZER_TOGGLE_CONTROLS.map(async (control) => {
|
|
297
|
+
const reply = await this.request(razerReadToggleControlCommand(control)).catch(() => null);
|
|
298
|
+
const state = reply ? razerDecodeToggleControl(control, reply) : null;
|
|
299
|
+
if (state)
|
|
300
|
+
mappings[control] = state;
|
|
301
|
+
}),
|
|
302
|
+
]);
|
|
303
|
+
// Every control failing means the transport does not answer class 0x02 at
|
|
304
|
+
// all — the cable (0x00c0) has never been tested against it, only the
|
|
305
|
+
// receiver. Collapse that to null so the card is hidden outright rather
|
|
306
|
+
// than rendered empty, the same way readLiftOff degrades.
|
|
307
|
+
return Object.keys(mappings).length > 0 ? mappings : null;
|
|
308
|
+
}
|
|
309
|
+
/** Probes once, then trusts what setButtonMapping leaves behind. */
|
|
310
|
+
async currentButtonMappings() {
|
|
311
|
+
if (!this.buttonMappingsKnown) {
|
|
312
|
+
this.buttonMappings = await this.readButtonMappings();
|
|
313
|
+
this.buttonMappingsKnown = true;
|
|
314
|
+
}
|
|
315
|
+
return this.buttonMappings;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Sets one control's mapping and confirms it by reading the same control
|
|
319
|
+
* back — not a formality here: this write silently no-ops under the driver's
|
|
320
|
+
* default transaction id, so a status-only check would have shipped a
|
|
321
|
+
* control that looked correct and did nothing. See
|
|
322
|
+
* `RAZER_BUTTON_TRANSACTION_ID`.
|
|
323
|
+
*/
|
|
324
|
+
async setButtonMapping(control, mapping) {
|
|
325
|
+
await this.request(razerSetButtonMappingCommand(control, mapping));
|
|
326
|
+
const reply = await this.request(razerReadButtonMappingCommand(control));
|
|
327
|
+
const confirmed = razerDecodeButtonMapping(reply);
|
|
328
|
+
if (confirmed !== mapping) {
|
|
329
|
+
throw new Error(`The mouse kept ${confirmed ?? "an unrecognised mapping"} for ${RAZER_BUTTON_CONTROL_LABEL[control]} instead of ${mapping}.`);
|
|
330
|
+
}
|
|
331
|
+
this.buttonMappings = { ...this.buttonMappings, [control]: confirmed };
|
|
332
|
+
this.buttonMappingsKnown = true;
|
|
333
|
+
return confirmed;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Sets a two-state control (Scroll Up/Down, the bottom sensitivity button)
|
|
337
|
+
* and confirms it the same way `setButtonMapping` does — read-back after
|
|
338
|
+
* write, not status alone. `label` must be the control's own enabled label
|
|
339
|
+
* or "Disabled"; `razerSetToggleControlCommand` throws otherwise.
|
|
340
|
+
*/
|
|
341
|
+
async setToggleControl(control, label) {
|
|
342
|
+
await this.request(razerSetToggleControlCommand(control, label));
|
|
343
|
+
const reply = await this.request(razerReadToggleControlCommand(control));
|
|
344
|
+
const confirmed = razerDecodeToggleControl(control, reply);
|
|
345
|
+
if (confirmed !== label) {
|
|
346
|
+
throw new Error(`The mouse kept ${confirmed ?? "an unrecognised state"} for ${RAZER_TOGGLE_CONTROL_INFO[control].label} instead of ${label}.`);
|
|
347
|
+
}
|
|
348
|
+
this.buttonMappings = { ...this.buttonMappings, [control]: confirmed };
|
|
349
|
+
this.buttonMappingsKnown = true;
|
|
350
|
+
return confirmed;
|
|
351
|
+
}
|
|
352
|
+
async setDpi(dpi, dpiY = dpi) {
|
|
353
|
+
const ceiling = this.maxDpi();
|
|
354
|
+
for (const value of [dpi, dpiY]) {
|
|
355
|
+
if (!Number.isInteger(value) || value < DPI_MIN || value > ceiling) {
|
|
356
|
+
throw new Error(`DPI must be a whole number between ${DPI_MIN} and ${ceiling.toLocaleString()}.`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
await this.request(razerSetDpiCommand(dpi, dpiY, this.dpiStorageByte()));
|
|
360
|
+
const confirmed = decodeDpi(await this.request(razerReadDpiCommand(this.dpiStorageByte())));
|
|
361
|
+
if (confirmed.x !== dpi || confirmed.y !== dpiY) {
|
|
362
|
+
throw new Error(`The mouse kept ${confirmed.x.toLocaleString()} DPI instead of ${dpi.toLocaleString()}.`);
|
|
363
|
+
}
|
|
364
|
+
return confirmed.x;
|
|
365
|
+
}
|
|
366
|
+
async setSleepTimeout(seconds) {
|
|
367
|
+
if (!Number.isInteger(seconds) || seconds < SLEEP_MIN_SECONDS || seconds > SLEEP_MAX_SECONDS) {
|
|
368
|
+
const minutes = SLEEP_MAX_SECONDS / SLEEP_STEP_SECONDS;
|
|
369
|
+
throw new Error(`Auto sleep must be between 1 and ${minutes} minutes.`);
|
|
370
|
+
}
|
|
371
|
+
await this.request(razerSetSleepTimeoutCommand(seconds));
|
|
372
|
+
const confirmed = decodeSleepTimeout(await this.request(RAZER_READ.sleepTimeout));
|
|
373
|
+
if (confirmed !== seconds) {
|
|
374
|
+
throw new Error(`The mouse kept ${confirmed} seconds instead of ${seconds}.`);
|
|
375
|
+
}
|
|
376
|
+
return confirmed;
|
|
377
|
+
}
|
|
378
|
+
async setLowPowerThreshold(percent) {
|
|
379
|
+
// A range rather than the offered list: the panel adds the mouse's own value
|
|
380
|
+
// when it sits off the five-point step, and that value must stay writable.
|
|
381
|
+
if (!Number.isInteger(percent) || percent < LOW_POWER_MIN_PERCENT || percent > LOW_POWER_MAX_PERCENT) {
|
|
382
|
+
throw new Error(`Low power mode must be between ${LOW_POWER_MIN_PERCENT}% and ${LOW_POWER_MAX_PERCENT}%.`);
|
|
383
|
+
}
|
|
384
|
+
// Asked of the mouse, not the panel. Staging a polling change repaints the
|
|
385
|
+
// control as disabled without withdrawing a threshold already queued behind
|
|
386
|
+
// it, so the disabled select cannot be what enforces this.
|
|
387
|
+
const pollingRateHz = await this.readPollingRateHz();
|
|
388
|
+
if (pollingRateHz > LOW_POWER_MAX_POLLING_HZ) {
|
|
389
|
+
throw new Error(`Low power mode is unavailable at ${pollingRateHz.toLocaleString()} Hz.`
|
|
390
|
+
+ ` Set the polling rate to ${LOW_POWER_MAX_POLLING_HZ.toLocaleString()} Hz or lower first.`);
|
|
391
|
+
}
|
|
392
|
+
await this.request(razerSetLowPowerThresholdCommand(percent));
|
|
393
|
+
const confirmed = decodeLowPowerThreshold(await this.request(RAZER_READ.lowPowerThreshold));
|
|
394
|
+
if (confirmed !== percent) {
|
|
395
|
+
throw new Error(`The mouse kept ${confirmed}% instead of ${percent}%.`);
|
|
396
|
+
}
|
|
397
|
+
return confirmed;
|
|
398
|
+
}
|
|
399
|
+
async setPollingRate(pollingRateHz) {
|
|
400
|
+
// Dock passthrough: discover the paired mouse's ladder before validating.
|
|
401
|
+
if (this.profile()?.probePollingRates === true && !this.discoveredPollingRates) {
|
|
402
|
+
await this.readPollingRateHz();
|
|
403
|
+
}
|
|
404
|
+
if (!this.getSupportedPollingRates().includes(pollingRateHz)) {
|
|
405
|
+
throw new Error(`This mouse does not support ${pollingRateHz.toLocaleString()} Hz on this connection.`);
|
|
406
|
+
}
|
|
407
|
+
const highRate = this.usesHighRatePolling();
|
|
408
|
+
await this.request(highRate
|
|
409
|
+
? razerSetExtendedPollingCommand(pollingRateHz)
|
|
410
|
+
: razerSetLegacyPollingCommand(pollingRateHz));
|
|
411
|
+
const commitTransactionId = highRate
|
|
412
|
+
? this.profile()?.extendedPollingCommitTransactionId
|
|
413
|
+
: undefined;
|
|
414
|
+
if (commitTransactionId !== undefined) {
|
|
415
|
+
const commit = razerSetExtendedPollingCommand(pollingRateHz);
|
|
416
|
+
commit.args = [0x01, commit.args?.[1] ?? 0x00];
|
|
417
|
+
await this.request(commit, commitTransactionId);
|
|
418
|
+
}
|
|
419
|
+
// Changing the rate briefly reconfigures the wireless link, and exchanges
|
|
420
|
+
// sent into that window come back corrupt or unanswered — the Viper V4 Pro
|
|
421
|
+
// driver documents the same pause. Let it settle before the read-back; any
|
|
422
|
+
// stragglers are then caught by `exchange`'s own retry.
|
|
423
|
+
if (this.isWireless())
|
|
424
|
+
await this.delay(POLLING_LINK_SETTLE_MS);
|
|
425
|
+
const confirmed = await this.readPollingRateHz();
|
|
426
|
+
if (confirmed !== pollingRateHz) {
|
|
427
|
+
throw new Error(`The mouse kept ${confirmed.toLocaleString()} Hz instead of ${pollingRateHz.toLocaleString()} Hz.`);
|
|
428
|
+
}
|
|
429
|
+
return confirmed;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Battery level, and the charging state when the mouse reports one.
|
|
433
|
+
*
|
|
434
|
+
* The two are separate commands and a mouse may answer the first without the
|
|
435
|
+
* second — the Basilisk X HyperSpeed reports a good level and refuses
|
|
436
|
+
* `0x07`/`0x84` as unsupported. Letting that take the level down with it threw
|
|
437
|
+
* away the reading the panel actually wanted; an unknown charging state is
|
|
438
|
+
* only a missing word in the battery caption.
|
|
439
|
+
*/
|
|
440
|
+
async readBattery() {
|
|
441
|
+
const level = await this.request(RAZER_READ.battery);
|
|
442
|
+
const charging = await this.request(RAZER_READ.charging).catch(() => null);
|
|
443
|
+
return {
|
|
444
|
+
percent: decodeBatteryPercent(level),
|
|
445
|
+
state: charging === null
|
|
446
|
+
? "Unknown"
|
|
447
|
+
: decodeCharging(charging) ? "Charging" : "Discharging",
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Reads the tracking level and the asymmetric lift-off/landing pair.
|
|
452
|
+
*
|
|
453
|
+
* Returns null when the mouse does not answer. Class `0x0b` has only ever
|
|
454
|
+
* been exercised on the receiver, so the cable may reject it, and a status
|
|
455
|
+
* read that throws takes the whole panel down rather than one control. The
|
|
456
|
+
* caller degrades instead.
|
|
457
|
+
*/
|
|
458
|
+
async readLiftOff() {
|
|
459
|
+
const reply = await this.request(RAZER_READ.liftOff).catch(() => null);
|
|
460
|
+
return reply ? decodeLiftOff(reply) : null;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Reports whether the mouse is currently in asymmetric mode, or null when it
|
|
464
|
+
* cannot be established.
|
|
465
|
+
*
|
|
466
|
+
* Nothing readable carries the mode — two 47-command captures differing only
|
|
467
|
+
* by it were byte-identical, and a full sweep found nothing. What does report
|
|
468
|
+
* it is the pair write's own status: refused `0x03` in symmetric mode,
|
|
469
|
+
* accepted `0x02` in asymmetric.
|
|
470
|
+
*
|
|
471
|
+
* That makes this a probe rather than a read, so it is written to disturb
|
|
472
|
+
* nothing. Re-sending the values the mirror already holds is value-preserving
|
|
473
|
+
* either way, and mode-preserving too: a refusal cannot switch the mode, and
|
|
474
|
+
* an acceptance re-selects the mode the mouse was already in.
|
|
475
|
+
*
|
|
476
|
+
* Call it once per connection, not on every refresh — it is still a write.
|
|
477
|
+
*/
|
|
478
|
+
async probeAsymmetric(current) {
|
|
479
|
+
// The firmware stores an inverted pair without complaint, and one session
|
|
480
|
+
// left it holding lift-off 2 with landing 26. Probing with that would throw
|
|
481
|
+
// in the command builder, so report "unknown" rather than guessing.
|
|
482
|
+
if (current.landing >= current.liftOff)
|
|
483
|
+
return null;
|
|
484
|
+
try {
|
|
485
|
+
await this.request(razerSetLiftOffCommand(current.liftOff, current.landing));
|
|
486
|
+
return true;
|
|
487
|
+
}
|
|
488
|
+
catch (error) {
|
|
489
|
+
if (error instanceof RazerProtocolError && error.status === RAZER_STATUS.failure)
|
|
490
|
+
return false;
|
|
491
|
+
return null;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Whether this model stores a separate lift-off/landing pair at all.
|
|
496
|
+
*
|
|
497
|
+
* Establishing the mode costs a *write*, so it is only attempted where the
|
|
498
|
+
* pair commands have been confirmed on hardware. A model that has not been
|
|
499
|
+
* connected keeps the plain three-stop tracking control instead, which costs
|
|
500
|
+
* reads only.
|
|
501
|
+
*/
|
|
502
|
+
hasAsymmetricLiftOff() {
|
|
503
|
+
return this.profile()?.asymmetricLiftOff === true;
|
|
504
|
+
}
|
|
505
|
+
/** Probes once, then trusts what the setters leave behind. */
|
|
506
|
+
async asymmetricMode(current) {
|
|
507
|
+
if (!this.asymmetricKnown) {
|
|
508
|
+
this.asymmetric = await this.probeAsymmetric(current);
|
|
509
|
+
this.asymmetricKnown = true;
|
|
510
|
+
}
|
|
511
|
+
return this.asymmetric;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Selects the symmetric tracking distance, which also takes the mouse out of
|
|
515
|
+
* asymmetric mode — it honours whichever store was written last, and there is
|
|
516
|
+
* no mode flag to clear.
|
|
517
|
+
*
|
|
518
|
+
* Named for the shell's driver contract rather than the vendor's wording; the
|
|
519
|
+
* vendor calls this the tracking distance.
|
|
520
|
+
*/
|
|
521
|
+
async setLiftOffDistance(distance) {
|
|
522
|
+
await this.request(razerSetTrackingDistanceCommand(distance));
|
|
523
|
+
const confirmed = decodeLiftOff(await this.request(RAZER_READ.liftOff));
|
|
524
|
+
if (confirmed.tracking !== distance) {
|
|
525
|
+
throw new Error(`The mouse kept ${confirmed.tracking ?? "an unknown"} tracking distance instead of ${distance}.`);
|
|
526
|
+
}
|
|
527
|
+
this.asymmetric = false;
|
|
528
|
+
this.asymmetricKnown = true;
|
|
529
|
+
return distance;
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Writes the asymmetric pair, and switches the mouse into asymmetric mode to
|
|
533
|
+
* do it. The tracking level shares the same reply but not the same write, so
|
|
534
|
+
* it is left as the mouse holds it.
|
|
535
|
+
*
|
|
536
|
+
* Landing is capped just below lift-off rather than rejected, because
|
|
537
|
+
* lowering lift-off past an already-set landing is ordinary use of a pair of
|
|
538
|
+
* controls and the vendor software caps its own slider the same way. The
|
|
539
|
+
* returned values are what the mouse ended up holding, not what was asked
|
|
540
|
+
* for, so a caller can render the cap rather than guess at it.
|
|
541
|
+
*/
|
|
542
|
+
async setLiftOff(liftOff, landing) {
|
|
543
|
+
const capped = Math.min(landing, razerMaxLanding(liftOff));
|
|
544
|
+
// Built before anything is sent, so a rejected value costs no device
|
|
545
|
+
// traffic and cannot leave the mouse switched into asymmetric mode over a
|
|
546
|
+
// write that never happened. `razerSetLiftOffCommand` validates lift-off
|
|
547
|
+
// before landing, so an out-of-range lift-off still reports itself rather
|
|
548
|
+
// than being masked by the landing it drags out of range here.
|
|
549
|
+
const command = razerSetLiftOffCommand(liftOff, capped);
|
|
550
|
+
// The pair write is refused in symmetric mode, so it is armed first. Units
|
|
551
|
+
// differ within the same "Mouse 1.14" version string: the 0x01 unlock is
|
|
552
|
+
// verified on the swept hardware, but a reporter's unit answered the armed
|
|
553
|
+
// pair write 0x03 in two separate sessions, so each refusal falls back to
|
|
554
|
+
// the next documented arm rather than giving up.
|
|
555
|
+
const arms = [
|
|
556
|
+
[razerEnableAsymmetricLiftOffCommand()],
|
|
557
|
+
[razerEnableAsymmetricLiftOffCanonicalCommand()],
|
|
558
|
+
[razerEnableSensorCalibrationCommand(), razerEnableAsymmetricLiftOffCommand()],
|
|
559
|
+
];
|
|
560
|
+
await this.writePairArmed(command, arms);
|
|
561
|
+
// Some units answer a refused pair write by still moving the stored pair;
|
|
562
|
+
// others leave it untouched. Either way the read-back is a genuine check.
|
|
563
|
+
const confirmed = decodeLiftOff(await this.request(RAZER_READ.liftOff));
|
|
564
|
+
if (confirmed.liftOff !== liftOff || confirmed.landing !== capped) {
|
|
565
|
+
throw new Error(`The mouse kept lift-off ${confirmed.liftOff} and landing ${confirmed.landing} instead of ${liftOff} and ${capped}.`);
|
|
566
|
+
}
|
|
567
|
+
this.asymmetric = true;
|
|
568
|
+
this.asymmetricKnown = true;
|
|
569
|
+
return confirmed;
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Sends one arm sequence (setting writes) followed by the pair write, and
|
|
573
|
+
* returns on the first success. A refused arm is safe to burn — the units
|
|
574
|
+
* that refuse it read the stored pair back unchanged, and the pair write
|
|
575
|
+
* that follows restores any pair a refusal may have disturbed. When every
|
|
576
|
+
* arm fails, the last refusal propagates so the caller sees the refused
|
|
577
|
+
* command rather than a guessed substitution.
|
|
578
|
+
*/
|
|
579
|
+
async writePairArmed(command, arms) {
|
|
580
|
+
let lastRefusal;
|
|
581
|
+
for (const sequence of arms) {
|
|
582
|
+
for (const write of sequence) {
|
|
583
|
+
await this.request(write);
|
|
584
|
+
}
|
|
585
|
+
try {
|
|
586
|
+
await this.request(command);
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
catch (error) {
|
|
590
|
+
if (error instanceof RazerProtocolError && error.status === RAZER_STATUS.failure) {
|
|
591
|
+
lastRefusal = error;
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
throw error;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
throw lastRefusal ?? new RazerProtocolError(`The mouse refused the lift-off write after ${arms.length} attempts.`);
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Wired answers only the legacy command and the receiver only the extended
|
|
601
|
+
* one, so ask for the expected one first and keep the other as a fallback.
|
|
602
|
+
* Asking in the wrong order costs a failed exchange on every refresh.
|
|
603
|
+
*
|
|
604
|
+
* Mouse Dock Pro omits a fixed rate list (`probePollingRates`): if extended
|
|
605
|
+
* polling answers, the paired mouse can use the HyperPolling ladder;
|
|
606
|
+
* otherwise stay on the 1 kHz ladder (e.g. Naga V2 Pro).
|
|
607
|
+
*/
|
|
608
|
+
async readPollingRateHz() {
|
|
609
|
+
const probe = this.profile()?.probePollingRates === true;
|
|
610
|
+
const extended = [RAZER_READ.pollingRateExtended, decodeExtendedPollingRate, true];
|
|
611
|
+
const legacy = [RAZER_READ.pollingRate, decodeLegacyPollingRate, false];
|
|
612
|
+
// Until a dock probe settles, prefer the product's declared encoding so the
|
|
613
|
+
// first try matches what verified HyperSpeed paths already expect.
|
|
614
|
+
const preferExtended = this.discoveredHighRatePolling ?? this.profile()?.highRatePolling ?? this.isWireless();
|
|
615
|
+
for (const [command, decode, isExtended] of preferExtended ? [extended, legacy] : [legacy, extended]) {
|
|
616
|
+
const reply = await this.request(command).catch(() => null);
|
|
617
|
+
if (!reply)
|
|
618
|
+
continue;
|
|
619
|
+
const hz = decode(reply);
|
|
620
|
+
if (probe) {
|
|
621
|
+
this.discoveredHighRatePolling = isExtended;
|
|
622
|
+
this.discoveredPollingRates = isExtended ? RATES_8K : RATES_1K;
|
|
623
|
+
}
|
|
624
|
+
return hz;
|
|
625
|
+
}
|
|
626
|
+
throw new Error("The mouse did not report a polling rate.");
|
|
627
|
+
}
|
|
628
|
+
once(key, read) {
|
|
629
|
+
const pending = this.staticReads.get(key);
|
|
630
|
+
if (pending)
|
|
631
|
+
return pending;
|
|
632
|
+
const started = read();
|
|
633
|
+
this.staticReads.set(key, started);
|
|
634
|
+
started.catch(() => this.staticReads.delete(key));
|
|
635
|
+
return started;
|
|
636
|
+
}
|
|
637
|
+
async request(command, transactionId) {
|
|
638
|
+
const run = this.queue.then(() => this.exchange(command, transactionId), () => this.exchange(command, transactionId));
|
|
639
|
+
this.queue = run.catch(() => undefined);
|
|
640
|
+
return await run;
|
|
641
|
+
}
|
|
642
|
+
async exchange(command, transactionIdOverride) {
|
|
643
|
+
await this.open();
|
|
644
|
+
// A command may pin its own transaction id — the class `0x02` button write
|
|
645
|
+
// silently no-ops under the shared one. An explicit call-site override
|
|
646
|
+
// still wins over both.
|
|
647
|
+
const transactionId = transactionIdOverride
|
|
648
|
+
?? command.transactionId
|
|
649
|
+
?? this.profile()?.transactionId
|
|
650
|
+
?? RAZER_TRANSACTION_ID;
|
|
651
|
+
const request = encodeRazerRequest(command, transactionId);
|
|
652
|
+
// A busy status means the mouse will answer this same request later, so the
|
|
653
|
+
// reads keep going without a re-send. A corrupt reply means the exchange
|
|
654
|
+
// itself was lost — the receiver can return garbage while it reconfigures
|
|
655
|
+
// the wireless link after a polling-rate change — so the send is retried
|
|
656
|
+
// from scratch. Retrying keeps one corrupt reply from failing the whole
|
|
657
|
+
// status read, which is what took the lift-off panel down after an 8,000 Hz
|
|
658
|
+
// switch.
|
|
659
|
+
for (let attempt = 0; attempt < RESPONSE_ATTEMPTS; attempt += 1) {
|
|
660
|
+
await this.write(command, request);
|
|
661
|
+
const reply = await this.awaitReply(command);
|
|
662
|
+
if (reply)
|
|
663
|
+
return reply;
|
|
664
|
+
}
|
|
665
|
+
throw new Error("The mouse stayed busy — it may be asleep or out of range.");
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Sends one protocol request as a feature report. Chrome reports a refused
|
|
669
|
+
* write as a bare `NotAllowedError` whose message is only "Failed to write
|
|
670
|
+
* feature report." with no hint about why, so the throw is wrapped with the
|
|
671
|
+
* troubleshooting paths that actually explain it: the control interface is
|
|
672
|
+
* held by another program (Razer Synapse), the interface granted on the
|
|
673
|
+
* wireless receiver is the pointer collection rather than the control one,
|
|
674
|
+
* macOS has denied the browser Input Monitoring access, or the OS/browser
|
|
675
|
+
* refuses writes to mouse-class collections outright.
|
|
676
|
+
*/
|
|
677
|
+
async write(command, request) {
|
|
678
|
+
try {
|
|
679
|
+
await this.device.sendFeatureReport(RAZER_REPORT_ID, request);
|
|
680
|
+
}
|
|
681
|
+
catch (error) {
|
|
682
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
683
|
+
throw new Error(`Chrome could not write the ${this.displayName()}'s feature report (command 0x${command.commandClass
|
|
684
|
+
.toString(16)}/${command.commandId.toString(16)}). `
|
|
685
|
+
+ "Quit Razer Synapse and try again — it holds the control interface. If it still fails, "
|
|
686
|
+
+ "re-add the device and pick the other Razer Viper interface on the receiver, and on macOS "
|
|
687
|
+
+ "allow the browser under System Settings → Privacy & Security → Input Monitoring. "
|
|
688
|
+
+ `(${detail})`);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Reads up to the response budget for one send. Returns the decoded reply, or
|
|
693
|
+
* null when the reply was corrupt or the budget ran out on busy replies — the
|
|
694
|
+
* caller re-sends on null, since a corrupt reply is a lost exchange rather
|
|
695
|
+
* than a status the mouse owns.
|
|
696
|
+
*/
|
|
697
|
+
async awaitReply(command) {
|
|
698
|
+
for (let read = 0; read < RESPONSE_ATTEMPTS; read += 1) {
|
|
699
|
+
await this.delay(RESPONSE_DELAY_MS);
|
|
700
|
+
const reply = this.copyDataView(await this.device.receiveFeatureReport(RAZER_REPORT_ID));
|
|
701
|
+
try {
|
|
702
|
+
return decodeRazerResponse(reply, command);
|
|
703
|
+
}
|
|
704
|
+
catch (error) {
|
|
705
|
+
if (!(error instanceof RazerProtocolError))
|
|
706
|
+
throw error;
|
|
707
|
+
if (error.status === RAZER_STATUS.busy)
|
|
708
|
+
continue;
|
|
709
|
+
// A bad checksum or wrong length carries no status, so it is transport
|
|
710
|
+
// corruption rather than an answer the mouse decided on.
|
|
711
|
+
if (error.status === null)
|
|
712
|
+
return null;
|
|
713
|
+
// Repeating a getter is safe; setters confirm through a later read and
|
|
714
|
+
// must not be sent twice when an earlier reply is still buffered.
|
|
715
|
+
if (error.stale && isRazerGetter(command))
|
|
716
|
+
return null;
|
|
717
|
+
throw error;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return null;
|
|
721
|
+
}
|
|
722
|
+
copyDataView(view) {
|
|
723
|
+
return new Uint8Array(view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength));
|
|
724
|
+
}
|
|
725
|
+
delay(milliseconds) {
|
|
726
|
+
return new Promise((resolve) => window.setTimeout(resolve, milliseconds));
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Last-resort device selection for when none of the granted collections
|
|
731
|
+
* matched `RazerHidClient.isSupported()`'s shape check. Rather than guess a
|
|
732
|
+
* new shape, this tries the protocol itself against every Razer-vendor
|
|
733
|
+
* collection Chrome granted and returns the client for whichever one
|
|
734
|
+
* actually answers.
|
|
735
|
+
*
|
|
736
|
+
* This exists because the shape check assumes every model's control channel
|
|
737
|
+
* is a single Generic Desktop Mouse collection (verified true so far for
|
|
738
|
+
* every model this driver has been confirmed against) — a real assumption a
|
|
739
|
+
* given browser/OS combination could break for a specific model without any
|
|
740
|
+
* code here changing, since what collections get exposed to WebHID at all is
|
|
741
|
+
* decided below this driver, by the platform. Kept off the normal detection
|
|
742
|
+
* path (`registry.ts`'s `DEVICE_DRIVERS` entry still uses `isSupported()`
|
|
743
|
+
* alone) because sending real protocol traffic to every candidate is real
|
|
744
|
+
* device I/O, not a cheap shape check, and only worth it once the cheap
|
|
745
|
+
* check has already failed to find anything.
|
|
746
|
+
*
|
|
747
|
+
* `sendFeatureReport`/`receiveFeatureReport` reject immediately for a
|
|
748
|
+
* collection with no feature-report capability at all, so a wrong candidate
|
|
749
|
+
* costs one failed call, not a hang — see `write()`'s try/catch above.
|
|
750
|
+
*/
|
|
751
|
+
export async function razerProbeControlInterface(devices) {
|
|
752
|
+
const candidates = devices.filter((device) => device.vendorId === VENDOR_ID.razer);
|
|
753
|
+
for (const device of candidates) {
|
|
754
|
+
const client = new RazerHidClient(device);
|
|
755
|
+
const tag = `[razer-probe] 0x${device.productId.toString(16).padStart(4, "0")} `
|
|
756
|
+
+ device.collections.map((c) => `${(c.usagePage ?? 0).toString(16)}:${c.usage ?? 0}`).join(",");
|
|
757
|
+
try {
|
|
758
|
+
await client.open();
|
|
759
|
+
if (await client.probeReachable()) {
|
|
760
|
+
console.info(`${tag} -> answered`);
|
|
761
|
+
return client;
|
|
762
|
+
}
|
|
763
|
+
console.info(`${tag} -> opened, no reply`);
|
|
764
|
+
}
|
|
765
|
+
catch (error) {
|
|
766
|
+
console.info(`${tag} -> ${error instanceof Error ? error.message : String(error)}`);
|
|
767
|
+
}
|
|
768
|
+
await client.close().catch(() => undefined);
|
|
769
|
+
}
|
|
770
|
+
return null;
|
|
771
|
+
}
|
|
772
|
+
//# sourceMappingURL=hid.js.map
|