@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,728 @@
|
|
|
1
|
+
import { VENDOR_ID } from "../vendors.js";
|
|
2
|
+
import { LAMZU_PRODUCTS } from "@openmouse/protocol/lamzu";
|
|
3
|
+
import { buildX11DpiReport, decodeX11DpiReport, nearestX11Dpi, X11_DPI_DEFAULT_ACTIVE, X11_DPI_DEFAULT_STAGES, X11_DPI_MAX, X11_DPI_MIN, X11_DPI_REPORT_ID, X11_DPI_STAGE_COUNT, X11_DPI_STEP, } from "./dpi.js";
|
|
4
|
+
// Attack Shark mice ship from multiple OEMs with different VIDs and protocols:
|
|
5
|
+
//
|
|
6
|
+
// 0x1d57 — R1, X11 family: HID feature reports, 250 ms cmd delay
|
|
7
|
+
// 0x25a7 — X3, X6, X8, X11 direct: GearHub-derived protocol (report 0, 64 B)
|
|
8
|
+
// 0x373e — R5 Ultra, R3 (Lamzu OEM) — usagePage 0xffff feature reports
|
|
9
|
+
//
|
|
10
|
+
// PIDs change between firmware revisions, so detection is collection-based,
|
|
11
|
+
// not PID-based. For 0x373e we exclude known Lamzu PIDs.
|
|
12
|
+
//
|
|
13
|
+
// Real X11 hardware (0x1d57, wired 0xfa55 and wireless 0xfa60) exposes NO
|
|
14
|
+
// feature reports to the browser on any of its four HID entries. Its config
|
|
15
|
+
// channel is USB interface 2 (a system-control/consumer composite; see the
|
|
16
|
+
// lsusb dump in dressedinblack5/attack-shark-x11-electron docs/descritors),
|
|
17
|
+
// and WebHID only surfaces the Consumer collection, which declares none. The
|
|
18
|
+
// config feature reports (0x06 polling, 0x04 DPI) are still reachable through
|
|
19
|
+
// the OS HID stack on interface 2's `&col04` sub-collection: node-hid does
|
|
20
|
+
// this on Windows with the stock input.inf (no Zadig/WinUSB), and the Tauri
|
|
21
|
+
// Desktop app's Rust hidapi adapter opens every sub-collection path too. The
|
|
22
|
+
// browser-side collection gate below therefore refuses these units, and the
|
|
23
|
+
// X11-family helper turns that refusal into a real explanation; the
|
|
24
|
+
// collections-less native branch writes the same packets over such an adapter.
|
|
25
|
+
//
|
|
26
|
+
// Protocol source: xb-bx/attack-shark-r1-driver (Odin)
|
|
27
|
+
// HarukaYamamoto0/attack-shark-x11-driver (TypeScript)
|
|
28
|
+
// qmk.top GearHub bundle (MU class — 0x25a7 protocol)
|
|
29
|
+
// Research credit: viix0dev
|
|
30
|
+
// ── VID constants ─────────────────────────────────────────────────────────
|
|
31
|
+
const VID_1D57 = 0x1d57; // R1 / X11 family
|
|
32
|
+
const VID_25A7 = VENDOR_ID.attackShark; // X3, X6, X8, X11 direct
|
|
33
|
+
const VID_373E = 0x373e; // Lamzu OEM (R5 Ultra, R3)
|
|
34
|
+
// ── 0x1d57 protocol (R1 / X11) ───────────────────────────────────────────
|
|
35
|
+
// Confirmed from open-source driver research.
|
|
36
|
+
const CMD_DELAY_MS = 300;
|
|
37
|
+
// Polling rate: feature report 0x06, 9 bytes.
|
|
38
|
+
// [len=0x09, 0x01, rate_byte, checksum, 0, 0, 0, 0]
|
|
39
|
+
// (The browser prepends the report ID 0x06 when calling sendFeatureReport.)
|
|
40
|
+
const POLLING_REPORT_ID = 0x06;
|
|
41
|
+
const POLLING_RATES_1D57 = [
|
|
42
|
+
[0x08, 125],
|
|
43
|
+
[0x04, 250],
|
|
44
|
+
[0x02, 500],
|
|
45
|
+
[0x01, 1000],
|
|
46
|
+
];
|
|
47
|
+
const DPI_READ_REPORT_ID = 0xa0;
|
|
48
|
+
// Battery arrives as input report with this 4-byte signature; byte 4 = %.
|
|
49
|
+
// The leading 0x03 is the HID report id of the battery packet.
|
|
50
|
+
const BATTERY_SIGNATURE = [0x03, 0x55, 0x40, 0x01];
|
|
51
|
+
const BATTERY_REPORT_ID = BATTERY_SIGNATURE[0];
|
|
52
|
+
// ── 0x25a7 protocol (GearHub / MU class) ─────────────────────────────────
|
|
53
|
+
// Reverse-engineered from the qmk.top GearHub web driver JS bundle.
|
|
54
|
+
// Uses HID feature reports on report ID 0x00, 64 bytes total.
|
|
55
|
+
// Commands are padded to 9 bytes; checksum goes in byte[7].
|
|
56
|
+
const REPORT_ID_25A7 = 0x00;
|
|
57
|
+
const REPORT_LEN_25A7 = 64;
|
|
58
|
+
const CMD_LEN_25A7 = 9;
|
|
59
|
+
const CMD_DELAY_25A7_MS = 100;
|
|
60
|
+
// Command IDs (MU class)
|
|
61
|
+
const FEA_CMD_GET_REV = 0x80; // Get firmware revision
|
|
62
|
+
const FEA_CMD_GET_DPI = 0xd4; // Get DPI slots (param: profile)
|
|
63
|
+
const FEA_CMD_SET_REPORT_RATE = 0x04; // Set polling rate
|
|
64
|
+
/** Polling-rate byte codes used by the 0x25a7 protocol. */
|
|
65
|
+
export const POLLING_CODES_25A7 = new Map([
|
|
66
|
+
[125, 0x08],
|
|
67
|
+
[250, 0x04],
|
|
68
|
+
[500, 0x02],
|
|
69
|
+
[1000, 0x01],
|
|
70
|
+
[2000, 0x84],
|
|
71
|
+
[4000, 0x82],
|
|
72
|
+
[8000, 0x81],
|
|
73
|
+
]);
|
|
74
|
+
// ── Collection helpers ─────────────────────────────────────────────────────
|
|
75
|
+
function hasFeatureReports(collection) {
|
|
76
|
+
if (collection.featureReports.length > 0)
|
|
77
|
+
return true;
|
|
78
|
+
return collection.children.some(hasFeatureReports);
|
|
79
|
+
}
|
|
80
|
+
function hasVendorControl(collection) {
|
|
81
|
+
if (collection.usagePage === 0xffff && collection.featureReports.length > 0)
|
|
82
|
+
return true;
|
|
83
|
+
return collection.children.some(hasVendorControl);
|
|
84
|
+
}
|
|
85
|
+
function declaresInputReport(collection, reportId) {
|
|
86
|
+
if (collection.inputReports.some((report) => report.reportId === reportId))
|
|
87
|
+
return true;
|
|
88
|
+
return collection.children.some((child) => declaresInputReport(child, reportId));
|
|
89
|
+
}
|
|
90
|
+
// ── X11 family (config is native-only; battery is readable) ──────────────
|
|
91
|
+
// Documented 0x1d57 PIDs: wired X11, wireless X11 receiver, R1.
|
|
92
|
+
const X11_FAMILY_PIDS = new Set([0xfa55, 0xfa60, 0xfa61]);
|
|
93
|
+
const X11_FAMILY_NAMES = new Map([
|
|
94
|
+
[0xfa55, "Attack Shark X11 (wired)"],
|
|
95
|
+
[0xfa60, "Attack Shark X11 (wireless receiver)"],
|
|
96
|
+
[0xfa61, "Attack Shark R1"],
|
|
97
|
+
]);
|
|
98
|
+
const X11_FAMILY_MODELS = new Map([
|
|
99
|
+
[0xfa55, "Attack Shark X11"],
|
|
100
|
+
[0xfa60, "Attack Shark X11"],
|
|
101
|
+
[0xfa61, "Attack Shark R1"],
|
|
102
|
+
]);
|
|
103
|
+
// The wireless receiver's interface 2 pushes battery packets on its own —
|
|
104
|
+
// no command needed — so a read-only claim of that entry costs nothing and
|
|
105
|
+
// risks nothing. The wired PIDs never report battery on this endpoint.
|
|
106
|
+
const X11_WIRELESS_PID = 0xfa60;
|
|
107
|
+
// The X11 family's DPI report 0x04 is documented for the X11 (wired 0xfa55,
|
|
108
|
+
// wireless 0xfa60). The R1 (0xfa61) uses a different DpiBuilder/step map in
|
|
109
|
+
// the reference driver, so it stays out of this path until ported.
|
|
110
|
+
const X11_DPI_PIDS = new Set([0xfa55, 0xfa60]);
|
|
111
|
+
const x11DpiStates = new Map();
|
|
112
|
+
function x11DpiStateFor(productId) {
|
|
113
|
+
let state = x11DpiStates.get(productId);
|
|
114
|
+
if (!state) {
|
|
115
|
+
state = {
|
|
116
|
+
stages: [...X11_DPI_DEFAULT_STAGES],
|
|
117
|
+
activeStage: X11_DPI_DEFAULT_ACTIVE,
|
|
118
|
+
angleSnap: false,
|
|
119
|
+
rippleControl: true,
|
|
120
|
+
};
|
|
121
|
+
x11DpiStates.set(productId, state);
|
|
122
|
+
}
|
|
123
|
+
return state;
|
|
124
|
+
}
|
|
125
|
+
/** Test/diagnostic hook: forget cached X11 DPI state (defaults come back). */
|
|
126
|
+
export function resetAttackSharkX11DpiState() {
|
|
127
|
+
x11DpiStates.clear();
|
|
128
|
+
}
|
|
129
|
+
// Battery and polling rate are both push/last-known rather than readable: the
|
|
130
|
+
// receiver streams `03 55 40 01 <pct>` on interface 2 on its own, and the
|
|
131
|
+
// firmware has no polling-rate read-back. This process keeps the last value it
|
|
132
|
+
// saw per product id so a short-lived client can still report it. The X11's
|
|
133
|
+
// reference driver and the web app's own X11 bridge both default the polling
|
|
134
|
+
// rate to 1,000 Hz for the same reason.
|
|
135
|
+
const X11_DEFAULT_POLLING_HZ = 1000;
|
|
136
|
+
/** How long a battery sample stays fresh enough to skip waiting for another. */
|
|
137
|
+
const X11_BATTERY_TTL_MS = 30_000;
|
|
138
|
+
/** Longest a status read waits for the receiver's next battery packet. */
|
|
139
|
+
const X11_BATTERY_WAIT_MS = 2_500;
|
|
140
|
+
const x11RuntimeStates = new Map();
|
|
141
|
+
function x11RuntimeFor(productId) {
|
|
142
|
+
let state = x11RuntimeStates.get(productId);
|
|
143
|
+
if (!state) {
|
|
144
|
+
state = { pollingRateHz: X11_DEFAULT_POLLING_HZ, batteryPercent: null, batteryAt: 0 };
|
|
145
|
+
x11RuntimeStates.set(productId, state);
|
|
146
|
+
}
|
|
147
|
+
return state;
|
|
148
|
+
}
|
|
149
|
+
/** Test/diagnostic hook: forget cached X11 battery/polling state. */
|
|
150
|
+
export function resetAttackSharkX11RuntimeState() {
|
|
151
|
+
x11RuntimeStates.clear();
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* If the granted devices include an X11-family unit that no driver could
|
|
155
|
+
* claim, explain why instead of letting the generic "not a control
|
|
156
|
+
* interface" error blame the picker choice. This fires only when the
|
|
157
|
+
* status entry (the composite with a Consumer collection) was not among
|
|
158
|
+
* the grants — the rows look identical in the picker, so say how to get
|
|
159
|
+
* the right one — and it stays honest about settings being native-only.
|
|
160
|
+
* Returns null when no X11-family device is present.
|
|
161
|
+
*/
|
|
162
|
+
export function attackSharkNativeOnlyMessage(devices) {
|
|
163
|
+
const unit = devices.find((device) => device.vendorId === VID_1D57 && X11_FAMILY_PIDS.has(device.productId));
|
|
164
|
+
if (!unit)
|
|
165
|
+
return null;
|
|
166
|
+
const name = X11_FAMILY_NAMES.get(unit.productId) ?? "Attack Shark X11";
|
|
167
|
+
return `This ${name} cannot be configured through the browser: its settings channel `
|
|
168
|
+
+ "is on an interface the browser is not allowed to reach. To change DPI, polling "
|
|
169
|
+
+ "rate and lighting, install the OpenMouse Bridge, then open Interface settings "
|
|
170
|
+
+ "→ Bridge → Native devices and click “Enable native control”.";
|
|
171
|
+
}
|
|
172
|
+
function detectFamily(device) {
|
|
173
|
+
if (device.vendorId === VID_1D57) {
|
|
174
|
+
// Native HID adapters (Tauri's TauriHidDevice, the Node/Bridge adapter)
|
|
175
|
+
// cannot parse the report descriptor and report no collections at all,
|
|
176
|
+
// so every collection-based gate below would refuse these units. On that
|
|
177
|
+
// transport the vendor feature reports are reachable, so identify the
|
|
178
|
+
// family from the vendor/product id instead. WebHID always reports the
|
|
179
|
+
// real collection tree, so this branch never fires in a browser.
|
|
180
|
+
if (device.collections.length === 0) {
|
|
181
|
+
return X11_FAMILY_PIDS.has(device.productId) ? "1d57-x11" : null;
|
|
182
|
+
}
|
|
183
|
+
// Some 0x1d57 mice (X8 SE, X11) use the GearHub protocol despite
|
|
184
|
+
// sharing the R1 VID. Distinguish by checking for a vendor-specific
|
|
185
|
+
// collection (usagePage 0xffff) which the GearHub interface exposes.
|
|
186
|
+
if (device.collections.some(hasVendorControl))
|
|
187
|
+
return "25a7";
|
|
188
|
+
if (device.collections.some(hasFeatureReports))
|
|
189
|
+
return "1d57";
|
|
190
|
+
// Real X11/R1 units declare no feature reports anywhere (see header
|
|
191
|
+
// note), so config writes are impossible here — but their interface-2
|
|
192
|
+
// entry, the composite with a Consumer top-level collection, carries the
|
|
193
|
+
// autonomous battery stream. Claim that one read-only; the plain boot
|
|
194
|
+
// keyboard/mouse entries stay refused.
|
|
195
|
+
if (X11_FAMILY_PIDS.has(device.productId)
|
|
196
|
+
&& device.collections.some((collection) => collection.usagePage === 0x0c)) {
|
|
197
|
+
return "1d57-x11";
|
|
198
|
+
}
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
if (device.vendorId === VID_25A7) {
|
|
202
|
+
// Same native-adapter reasoning as VID_1D57 above: with no report
|
|
203
|
+
// descriptor to inspect, the vendor id alone identifies the GearHub
|
|
204
|
+
// family.
|
|
205
|
+
if (device.collections.length === 0)
|
|
206
|
+
return "25a7";
|
|
207
|
+
return device.collections.some(hasVendorControl) ? "25a7" : null;
|
|
208
|
+
}
|
|
209
|
+
if (device.vendorId === VID_373E) {
|
|
210
|
+
if (LAMZU_PRODUCTS.has(device.productId))
|
|
211
|
+
return null;
|
|
212
|
+
return device.collections.some(hasVendorControl) ? "373e" : null;
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
// ── 0x25a7 helpers ────────────────────────────────────────────────────────
|
|
217
|
+
/**
|
|
218
|
+
* Compute the GearHub checksum: one's-complement of the sum of the first 7
|
|
219
|
+
* command bytes, stored in byte 7. Byte 8 stays zero (unused padding).
|
|
220
|
+
*/
|
|
221
|
+
export function checksum25a7(cmd) {
|
|
222
|
+
const out = new Uint8Array(CMD_LEN_25A7);
|
|
223
|
+
out.set(cmd.subarray(0, Math.min(cmd.length, CMD_LEN_25A7)));
|
|
224
|
+
let sum = 0;
|
|
225
|
+
for (let i = 0; i < 7; i++)
|
|
226
|
+
sum = (sum + out[i]) & 0xff;
|
|
227
|
+
out[7] = (0xff - sum) & 0xff;
|
|
228
|
+
return out;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Encode a 64-byte HID report that starts with the 9-byte padded+checksummed
|
|
232
|
+
* command and is zero-padded to REPORT_LEN_25A7.
|
|
233
|
+
*/
|
|
234
|
+
function encodeReport25a7(cmd) {
|
|
235
|
+
const report = new Uint8Array(REPORT_LEN_25A7);
|
|
236
|
+
report.set(checksum25a7(cmd));
|
|
237
|
+
return report;
|
|
238
|
+
}
|
|
239
|
+
/** Lookup a polling-rate Hz value and return its byte code. */
|
|
240
|
+
function pollingHzToCode25a7(hz) {
|
|
241
|
+
return POLLING_CODES_25A7.get(hz);
|
|
242
|
+
}
|
|
243
|
+
// ── Driver ────────────────────────────────────────────────────────────────
|
|
244
|
+
export class AttackSharkHidClient {
|
|
245
|
+
device;
|
|
246
|
+
family;
|
|
247
|
+
batteryWaitMs;
|
|
248
|
+
lastStatus = null;
|
|
249
|
+
queue = Promise.resolve();
|
|
250
|
+
batteryPercent = null;
|
|
251
|
+
listening = false;
|
|
252
|
+
constructor(device, options = {}) {
|
|
253
|
+
this.device = device;
|
|
254
|
+
this.family = detectFamily(device);
|
|
255
|
+
this.batteryWaitMs = options.batteryWaitMs ?? X11_BATTERY_WAIT_MS;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* True when this client is running over a native HID adapter (Tauri's
|
|
259
|
+
* TauriHidDevice, the Node/Bridge adapter) rather than WebHID. Those
|
|
260
|
+
* adapters report no collections, but unlike the browser they can reach
|
|
261
|
+
* the vendor feature reports, so the X11 config channel is writable
|
|
262
|
+
* through them.
|
|
263
|
+
*/
|
|
264
|
+
get nativeConfig() {
|
|
265
|
+
return this.device.collections.length === 0;
|
|
266
|
+
}
|
|
267
|
+
/** True when this unit's DPI report 0x04 can be driven over the native channel. */
|
|
268
|
+
get x11DpiSupported() {
|
|
269
|
+
return this.family === "1d57-x11"
|
|
270
|
+
&& this.nativeConfig
|
|
271
|
+
&& X11_DPI_PIDS.has(this.device.productId);
|
|
272
|
+
}
|
|
273
|
+
static isSupported(device) {
|
|
274
|
+
return detectFamily(device) !== null;
|
|
275
|
+
}
|
|
276
|
+
// Battery packets arrive as inputreport events; the raw packet's leading
|
|
277
|
+
// 0x03 is the HID report id, which WebHID strips into event.reportId, so
|
|
278
|
+
// rebuild the native shape before matching the signature.
|
|
279
|
+
onInputReport = (event) => {
|
|
280
|
+
const data = new Uint8Array(event.data.buffer, event.data.byteOffset, event.data.byteLength);
|
|
281
|
+
const packet = new Uint8Array(data.length + 1);
|
|
282
|
+
packet[0] = event.reportId;
|
|
283
|
+
packet.set(data, 1);
|
|
284
|
+
const percent = AttackSharkHidClient.parseBatteryReport(packet);
|
|
285
|
+
if (percent !== null) {
|
|
286
|
+
this.batteryPercent = percent;
|
|
287
|
+
const runtime = x11RuntimeFor(this.device.productId);
|
|
288
|
+
runtime.batteryPercent = percent;
|
|
289
|
+
runtime.batteryAt = Date.now();
|
|
290
|
+
if (this.lastStatus) {
|
|
291
|
+
this.lastStatus = { ...this.lastStatus, batteryPercent: percent, batteryState: "Discharging" };
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
async open() {
|
|
296
|
+
if (!this.device.opened)
|
|
297
|
+
await this.device.open();
|
|
298
|
+
if (this.family === "1d57-x11" && !this.listening) {
|
|
299
|
+
this.device.addEventListener("inputreport", this.onInputReport);
|
|
300
|
+
this.listening = true;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
async close() {
|
|
304
|
+
this.lastStatus = null;
|
|
305
|
+
if (this.listening) {
|
|
306
|
+
this.device.removeEventListener("inputreport", this.onInputReport);
|
|
307
|
+
this.listening = false;
|
|
308
|
+
}
|
|
309
|
+
if (this.device.opened)
|
|
310
|
+
await this.device.close();
|
|
311
|
+
}
|
|
312
|
+
async startNotifications() {
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
displayName() {
|
|
316
|
+
// X11-family product strings are generic OEM labels ("2.4G Wireless
|
|
317
|
+
// Device", "USB Gaming Mouse"), so name those models by PID instead.
|
|
318
|
+
const model = this.device.vendorId === VID_1D57
|
|
319
|
+
? X11_FAMILY_MODELS.get(this.device.productId)
|
|
320
|
+
: undefined;
|
|
321
|
+
if (model)
|
|
322
|
+
return model;
|
|
323
|
+
const name = this.device.productName?.trim();
|
|
324
|
+
if (!name)
|
|
325
|
+
return "Attack Shark";
|
|
326
|
+
return /^attack\s*shark/i.test(name) ? name : `Attack Shark ${name}`;
|
|
327
|
+
}
|
|
328
|
+
deviceBrand() {
|
|
329
|
+
return "Attack Shark";
|
|
330
|
+
}
|
|
331
|
+
isWireless() {
|
|
332
|
+
if (this.device.vendorId === VID_1D57 && X11_FAMILY_PIDS.has(this.device.productId)) {
|
|
333
|
+
return this.device.productId === X11_WIRELESS_PID;
|
|
334
|
+
}
|
|
335
|
+
return /receiver|dongle|wireless|2\.4g/i.test(this.device.productName || "");
|
|
336
|
+
}
|
|
337
|
+
getSupportedPollingRates() {
|
|
338
|
+
if (this.family === "1d57")
|
|
339
|
+
return POLLING_RATES_1D57.map(([, hz]) => hz);
|
|
340
|
+
if (this.family === "25a7")
|
|
341
|
+
return [...POLLING_CODES_25A7.keys()];
|
|
342
|
+
// Native transport: the X11 exposes the same 0x06 polling command the
|
|
343
|
+
// browser cannot reach, so advertise the rates it accepts.
|
|
344
|
+
if (this.family === "1d57-x11" && this.nativeConfig)
|
|
345
|
+
return POLLING_RATES_1D57.map(([, hz]) => hz);
|
|
346
|
+
return [];
|
|
347
|
+
}
|
|
348
|
+
getDpiOptions() {
|
|
349
|
+
// 25a7 devices expose up to 8 DPI slots per profile.
|
|
350
|
+
// The actual values are read dynamically in readStatus(); we return a
|
|
351
|
+
// standard set of supported DPI steps so the UI can offer them.
|
|
352
|
+
if (this.family === "25a7") {
|
|
353
|
+
return [400, 800, 1200, 1600, 2400, 3200, 6400, 12000, 26000];
|
|
354
|
+
}
|
|
355
|
+
return [];
|
|
356
|
+
}
|
|
357
|
+
async readStatus() {
|
|
358
|
+
await this.open();
|
|
359
|
+
let pollingRateHz = 0;
|
|
360
|
+
let dpi = 0;
|
|
361
|
+
let firmware = [];
|
|
362
|
+
if (this.family === "1d57") {
|
|
363
|
+
pollingRateHz = await this.read1d57PollingRate().catch(() => 0);
|
|
364
|
+
}
|
|
365
|
+
if (this.family === "25a7") {
|
|
366
|
+
const result = await this.read25a7Status().catch(() => null);
|
|
367
|
+
if (result) {
|
|
368
|
+
pollingRateHz = result.pollingRateHz;
|
|
369
|
+
dpi = result.dpi;
|
|
370
|
+
firmware = result.firmware;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
const dpiState = this.x11DpiSupported ? x11DpiStateFor(this.device.productId) : null;
|
|
374
|
+
if (dpiState) {
|
|
375
|
+
await this.readX11DpiState();
|
|
376
|
+
dpi = dpiState.stages[dpiState.activeStage - 1] ?? 0;
|
|
377
|
+
}
|
|
378
|
+
// Native X11: polling rate has no read-back (report the last value this
|
|
379
|
+
// process applied, defaulting to 1,000 Hz), and the wireless receiver
|
|
380
|
+
// pushes battery on its own — give it a bounded moment to arrive.
|
|
381
|
+
const x11Runtime = this.family === "1d57-x11" && this.nativeConfig
|
|
382
|
+
? x11RuntimeFor(this.device.productId)
|
|
383
|
+
: null;
|
|
384
|
+
if (x11Runtime) {
|
|
385
|
+
if (this.isWireless())
|
|
386
|
+
await this.waitForX11Battery(x11Runtime);
|
|
387
|
+
pollingRateHz = x11Runtime.pollingRateHz;
|
|
388
|
+
}
|
|
389
|
+
const batteryPercent = x11Runtime ? x11Runtime.batteryPercent : this.batteryPercent;
|
|
390
|
+
return this.lastStatus = {
|
|
391
|
+
brand: "Attack Shark",
|
|
392
|
+
name: this.displayName(),
|
|
393
|
+
ui: {
|
|
394
|
+
family: "attackshark",
|
|
395
|
+
settingsReady: this.family === "1d57"
|
|
396
|
+
|| this.family === "25a7"
|
|
397
|
+
|| (this.family === "1d57-x11" && this.nativeConfig),
|
|
398
|
+
hideUnsupportedPollingRates: true,
|
|
399
|
+
hideProcessingCard: true,
|
|
400
|
+
// Wireless X11-family units push battery on their own. WebHID only
|
|
401
|
+
// shows the column when the browser can see the battery input report
|
|
402
|
+
// (usually hidden under the protected system-control collection); a
|
|
403
|
+
// native adapter delivers the stream directly, so it is always worth
|
|
404
|
+
// showing there.
|
|
405
|
+
forceShowBattery: this.family === "1d57-x11"
|
|
406
|
+
&& this.isWireless()
|
|
407
|
+
&& (this.nativeConfig
|
|
408
|
+
|| this.device.collections.some((collection) => declaresInputReport(collection, BATTERY_REPORT_ID))),
|
|
409
|
+
statusNote: this.family === "1d57-x11" && !this.nativeConfig
|
|
410
|
+
? "Status only: this mouse's settings channel is not reachable from a browser and needs a native driver."
|
|
411
|
+
: undefined,
|
|
412
|
+
...(dpiState
|
|
413
|
+
? {
|
|
414
|
+
dpiStageEditor: {
|
|
415
|
+
maxStages: X11_DPI_STAGE_COUNT,
|
|
416
|
+
countEditable: false,
|
|
417
|
+
minDpi: X11_DPI_MIN,
|
|
418
|
+
maxDpi: X11_DPI_MAX,
|
|
419
|
+
stepDpi: X11_DPI_STEP,
|
|
420
|
+
},
|
|
421
|
+
}
|
|
422
|
+
: {}),
|
|
423
|
+
},
|
|
424
|
+
batteryPercent,
|
|
425
|
+
batteryState: batteryPercent !== null ? "Discharging" : "Unknown",
|
|
426
|
+
dpi,
|
|
427
|
+
...(dpiState
|
|
428
|
+
? {
|
|
429
|
+
dpiStages: [...dpiState.stages],
|
|
430
|
+
activeDpiStage: dpiState.activeStage - 1,
|
|
431
|
+
angleSnapping: dpiState.angleSnap,
|
|
432
|
+
rippleControl: dpiState.rippleControl,
|
|
433
|
+
}
|
|
434
|
+
: {}),
|
|
435
|
+
pollingRateHz,
|
|
436
|
+
supportedPollingRates: this.getSupportedPollingRates(),
|
|
437
|
+
activeProfile: null,
|
|
438
|
+
connectionType: this.isWireless() ? "Wireless" : "Wired",
|
|
439
|
+
liftOffDistance: null,
|
|
440
|
+
firmware,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
async setPollingRate(pollingRateHz) {
|
|
444
|
+
if (this.family === "1d57-x11") {
|
|
445
|
+
if (!this.nativeConfig) {
|
|
446
|
+
throw new Error("This mouse's settings channel is not reachable from a browser; "
|
|
447
|
+
+ "changing settings needs the native Attack Shark X11 driver.");
|
|
448
|
+
}
|
|
449
|
+
// Native transport: the same 0x06 feature report the 0x1d57 (R1) path
|
|
450
|
+
// uses. This firmware exposes no read-back command, so trust the write
|
|
451
|
+
// and cache the value rather than confirming it.
|
|
452
|
+
const entry = POLLING_RATES_1D57.find(([, hz]) => hz === pollingRateHz);
|
|
453
|
+
if (!entry)
|
|
454
|
+
throw new Error(`This mouse does not support ${pollingRateHz} Hz.`);
|
|
455
|
+
await this.write1d57PollingRate(entry[0]);
|
|
456
|
+
x11RuntimeFor(this.device.productId).pollingRateHz = pollingRateHz;
|
|
457
|
+
if (this.lastStatus)
|
|
458
|
+
this.lastStatus = { ...this.lastStatus, pollingRateHz };
|
|
459
|
+
return pollingRateHz;
|
|
460
|
+
}
|
|
461
|
+
if (this.family === "1d57") {
|
|
462
|
+
const entry = POLLING_RATES_1D57.find(([, hz]) => hz === pollingRateHz);
|
|
463
|
+
if (!entry)
|
|
464
|
+
throw new Error(`This mouse does not support ${pollingRateHz} Hz.`);
|
|
465
|
+
await this.write1d57PollingRate(entry[0]);
|
|
466
|
+
const confirmed = await this.read1d57PollingRate();
|
|
467
|
+
if (confirmed !== pollingRateHz) {
|
|
468
|
+
throw new Error(`The mouse kept ${confirmed} Hz instead of ${pollingRateHz} Hz.`);
|
|
469
|
+
}
|
|
470
|
+
if (this.lastStatus)
|
|
471
|
+
this.lastStatus = { ...this.lastStatus, pollingRateHz: confirmed };
|
|
472
|
+
return confirmed;
|
|
473
|
+
}
|
|
474
|
+
if (this.family === "25a7") {
|
|
475
|
+
const code = pollingHzToCode25a7(pollingRateHz);
|
|
476
|
+
if (code === undefined)
|
|
477
|
+
throw new Error(`This mouse does not support ${pollingRateHz} Hz.`);
|
|
478
|
+
await this.write25a7PollingRate(code);
|
|
479
|
+
if (this.lastStatus)
|
|
480
|
+
this.lastStatus = { ...this.lastStatus, pollingRateHz };
|
|
481
|
+
return pollingRateHz;
|
|
482
|
+
}
|
|
483
|
+
throw new Error("Polling rate control is not yet implemented for this Attack Shark model.");
|
|
484
|
+
}
|
|
485
|
+
// ── X11 DPI (report 0x04) ─────────────────────────────────────────────
|
|
486
|
+
/** Throw the same explanation the browser path gives when DPI cannot be driven. */
|
|
487
|
+
requireX11Dpi() {
|
|
488
|
+
if (!this.x11DpiSupported) {
|
|
489
|
+
throw new Error(this.family === "1d57-x11"
|
|
490
|
+
? "This mouse's settings channel is not reachable from a browser; "
|
|
491
|
+
+ "changing DPI needs the native Attack Shark X11 driver."
|
|
492
|
+
: "DPI control is not yet implemented for this Attack Shark model.");
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Re-send the full six-stage table. The firmware has no partial update and
|
|
497
|
+
* no reliable read-back, so every edit carries the whole table — the same
|
|
498
|
+
* approach the reference driver takes.
|
|
499
|
+
*/
|
|
500
|
+
async writeX11Dpi() {
|
|
501
|
+
const state = x11DpiStateFor(this.device.productId);
|
|
502
|
+
const report = buildX11DpiReport({
|
|
503
|
+
stages: state.stages,
|
|
504
|
+
activeStage: state.activeStage,
|
|
505
|
+
angleSnap: state.angleSnap,
|
|
506
|
+
rippleControl: state.rippleControl,
|
|
507
|
+
wired: this.device.productId === 0xfa55,
|
|
508
|
+
});
|
|
509
|
+
// The buffer's leading byte is the report id; WebHID/Tauri take it
|
|
510
|
+
// separately. Copy so the payload is a plain ArrayBuffer-backed view.
|
|
511
|
+
const payload = new Uint8Array(report.length - 1);
|
|
512
|
+
payload.set(report.subarray(1));
|
|
513
|
+
await this.run(() => this.device.sendFeatureReport(X11_DPI_REPORT_ID, payload));
|
|
514
|
+
await this.delay(CMD_DELAY_MS);
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Best-effort read of the live table. The reference driver only documents
|
|
518
|
+
* this for the wireless receiver (a GET on report 0x04); the wired unit
|
|
519
|
+
* returns nothing. A failure leaves the cached table untouched rather than
|
|
520
|
+
* aborting the status read.
|
|
521
|
+
*/
|
|
522
|
+
async readX11DpiState() {
|
|
523
|
+
if (this.device.productId === 0xfa55)
|
|
524
|
+
return;
|
|
525
|
+
const state = x11DpiStateFor(this.device.productId);
|
|
526
|
+
try {
|
|
527
|
+
const view = await this.run(() => this.device.receiveFeatureReport(X11_DPI_REPORT_ID));
|
|
528
|
+
const bytes = new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
|
|
529
|
+
const decoded = decodeX11DpiReport(bytes);
|
|
530
|
+
if (!decoded)
|
|
531
|
+
return;
|
|
532
|
+
state.stages = [...decoded.stages];
|
|
533
|
+
state.activeStage = decoded.activeStage;
|
|
534
|
+
state.angleSnap = decoded.angleSnap;
|
|
535
|
+
state.rippleControl = decoded.rippleControl;
|
|
536
|
+
}
|
|
537
|
+
catch {
|
|
538
|
+
// No read-back on this firmware/transport; keep the cached table.
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* The wireless receiver streams battery on its own, so the only way to get
|
|
543
|
+
* a fresh percentage is to stay open for its next packet. Skip the wait when
|
|
544
|
+
* the last sample is still fresh — the desktop app re-reads every few
|
|
545
|
+
* seconds and should not pay this each time.
|
|
546
|
+
*/
|
|
547
|
+
async waitForX11Battery(runtime) {
|
|
548
|
+
if (runtime.batteryPercent !== null && Date.now() - runtime.batteryAt < X11_BATTERY_TTL_MS)
|
|
549
|
+
return;
|
|
550
|
+
const start = Date.now();
|
|
551
|
+
const deadline = start + this.batteryWaitMs;
|
|
552
|
+
while (Date.now() < deadline) {
|
|
553
|
+
await this.delay(100);
|
|
554
|
+
if (runtime.batteryPercent !== null && runtime.batteryAt >= start)
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
/** Sets the active stage's DPI, preserving the other five. */
|
|
559
|
+
async setDpi(dpi, _dpiY) {
|
|
560
|
+
this.requireX11Dpi();
|
|
561
|
+
const state = x11DpiStateFor(this.device.productId);
|
|
562
|
+
const value = nearestX11Dpi(dpi);
|
|
563
|
+
state.stages[state.activeStage - 1] = value;
|
|
564
|
+
await this.writeX11Dpi();
|
|
565
|
+
if (this.lastStatus)
|
|
566
|
+
this.lastStatus = { ...this.lastStatus, dpi: value };
|
|
567
|
+
return value;
|
|
568
|
+
}
|
|
569
|
+
/** Edits one stage's DPI. `stage` is 0-based, matching the shared UI contract. */
|
|
570
|
+
async setDpiStageValue(stage, dpi) {
|
|
571
|
+
this.requireX11Dpi();
|
|
572
|
+
if (!Number.isInteger(stage) || stage < 0 || stage >= X11_DPI_STAGE_COUNT) {
|
|
573
|
+
throw new RangeError(`This mouse has no DPI stage ${stage + 1}.`);
|
|
574
|
+
}
|
|
575
|
+
const state = x11DpiStateFor(this.device.productId);
|
|
576
|
+
const value = nearestX11Dpi(dpi);
|
|
577
|
+
state.stages[stage] = value;
|
|
578
|
+
await this.writeX11Dpi();
|
|
579
|
+
if (this.lastStatus)
|
|
580
|
+
this.lastStatus = { ...this.lastStatus, dpiStages: [...state.stages] };
|
|
581
|
+
return value;
|
|
582
|
+
}
|
|
583
|
+
/** Selects the active stage. `stage` is 0-based, matching the shared UI contract. */
|
|
584
|
+
async setActiveDpiStage(stage) {
|
|
585
|
+
this.requireX11Dpi();
|
|
586
|
+
if (!Number.isInteger(stage) || stage < 0 || stage >= X11_DPI_STAGE_COUNT) {
|
|
587
|
+
throw new RangeError(`This mouse has no DPI stage ${stage + 1}.`);
|
|
588
|
+
}
|
|
589
|
+
const state = x11DpiStateFor(this.device.productId);
|
|
590
|
+
state.activeStage = stage + 1;
|
|
591
|
+
await this.writeX11Dpi();
|
|
592
|
+
if (this.lastStatus) {
|
|
593
|
+
this.lastStatus = {
|
|
594
|
+
...this.lastStatus,
|
|
595
|
+
dpi: state.stages[stage] ?? this.lastStatus.dpi,
|
|
596
|
+
activeDpiStage: stage,
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
return stage;
|
|
600
|
+
}
|
|
601
|
+
async setAngleSnapping(enabled) {
|
|
602
|
+
this.requireX11Dpi();
|
|
603
|
+
x11DpiStateFor(this.device.productId).angleSnap = enabled;
|
|
604
|
+
await this.writeX11Dpi();
|
|
605
|
+
if (this.lastStatus)
|
|
606
|
+
this.lastStatus = { ...this.lastStatus, angleSnapping: enabled };
|
|
607
|
+
return enabled;
|
|
608
|
+
}
|
|
609
|
+
async setRippleControl(enabled) {
|
|
610
|
+
this.requireX11Dpi();
|
|
611
|
+
x11DpiStateFor(this.device.productId).rippleControl = enabled;
|
|
612
|
+
await this.writeX11Dpi();
|
|
613
|
+
if (this.lastStatus)
|
|
614
|
+
this.lastStatus = { ...this.lastStatus, rippleControl: enabled };
|
|
615
|
+
return enabled;
|
|
616
|
+
}
|
|
617
|
+
// ── 0x25a7 low-level ──────────────────────────────────────────────────
|
|
618
|
+
/**
|
|
619
|
+
* Send a 9-byte command via a 64-byte HID feature report (report ID 0x00),
|
|
620
|
+
* then wait a short delay for the device to process it.
|
|
621
|
+
*/
|
|
622
|
+
async sendCmd25a7(cmd) {
|
|
623
|
+
const report = encodeReport25a7(cmd);
|
|
624
|
+
await this.run(() => this.device.sendFeatureReport(REPORT_ID_25A7, report));
|
|
625
|
+
await this.delay(CMD_DELAY_25A7_MS);
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Send a command and read back the 64-byte feature report reply.
|
|
629
|
+
*/
|
|
630
|
+
async askCmd25a7(cmd) {
|
|
631
|
+
await this.sendCmd25a7(cmd);
|
|
632
|
+
const reply = await this.run(() => this.device.receiveFeatureReport(REPORT_ID_25A7));
|
|
633
|
+
return this.copyView(reply);
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Get firmware revision string(s) from the device.
|
|
637
|
+
* Command 0x80 → response byte[1..2] = version (little-endian uint16).
|
|
638
|
+
*/
|
|
639
|
+
async getFirmware25a7() {
|
|
640
|
+
const resp = await this.askCmd25a7(new Uint8Array([FEA_CMD_GET_REV]));
|
|
641
|
+
if (resp[0] !== FEA_CMD_GET_REV)
|
|
642
|
+
return [];
|
|
643
|
+
const version = resp[1] | (resp[2] << 8);
|
|
644
|
+
return version !== 0 ? [`v${version}`] : [];
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Get DPI configuration for a profile.
|
|
648
|
+
* Command 0xD4 [profile] → response contains active DPI index, slot count,
|
|
649
|
+
* and per-slot X/Y values encoded as LE uint16.
|
|
650
|
+
*/
|
|
651
|
+
async getDpi25a7(profile) {
|
|
652
|
+
const resp = await this.askCmd25a7(new Uint8Array([FEA_CMD_GET_DPI, profile]));
|
|
653
|
+
if (resp[0] !== FEA_CMD_GET_DPI)
|
|
654
|
+
return { activeIndex: 0, slots: 0, dpis: [] };
|
|
655
|
+
const activeIndex = resp[2] > 8 ? 0 : resp[2];
|
|
656
|
+
const slotCount = resp[3];
|
|
657
|
+
const dpis = [];
|
|
658
|
+
for (let i = 0; i < slotCount; i++) {
|
|
659
|
+
const x = resp[8 + i * 2] | (resp[9 + i * 2] << 8);
|
|
660
|
+
dpis.push(x);
|
|
661
|
+
}
|
|
662
|
+
return { activeIndex, slots: slotCount, dpis };
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Set polling rate via command 0x04.
|
|
666
|
+
*/
|
|
667
|
+
async write25a7PollingRate(code) {
|
|
668
|
+
await this.sendCmd25a7(new Uint8Array([FEA_CMD_SET_REPORT_RATE, 0, code]));
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Read all status from a 0x25a7 device (firmware + DPI + polling rate).
|
|
672
|
+
*/
|
|
673
|
+
async read25a7Status() {
|
|
674
|
+
const firmware = await this.getFirmware25a7();
|
|
675
|
+
// Read DPI from profile 0
|
|
676
|
+
const dpiResult = await this.getDpi25a7(0);
|
|
677
|
+
const dpi = dpiResult.dpis[dpiResult.activeIndex] ?? 0;
|
|
678
|
+
// Polling rate is not directly readable via a single command in the MU
|
|
679
|
+
// class protocol; we default to 1000 Hz and let the user set it.
|
|
680
|
+
const pollingRateHz = 1000;
|
|
681
|
+
return { pollingRateHz, dpi, firmware };
|
|
682
|
+
}
|
|
683
|
+
// ── 0x1d57 low-level ──────────────────────────────────────────────────
|
|
684
|
+
async write1d57PollingRate(rateByte) {
|
|
685
|
+
await this.open();
|
|
686
|
+
// 8 data bytes — browser prepends report ID 0x06.
|
|
687
|
+
// Structure: [0x09, 0x01, rate, checksum, 0, 0, 0, 0]
|
|
688
|
+
const data = new Uint8Array([0x09, 0x01, rateByte, (0xff - rateByte) & 0xff, 0, 0, 0, 0]);
|
|
689
|
+
await this.run(() => this.device.sendFeatureReport(POLLING_REPORT_ID, data));
|
|
690
|
+
await this.delay(CMD_DELAY_MS);
|
|
691
|
+
}
|
|
692
|
+
async read1d57PollingRate() {
|
|
693
|
+
await this.open();
|
|
694
|
+
// Send read-request on report 0xa0 then read back from 0x06.
|
|
695
|
+
const req = new Uint8Array([POLLING_REPORT_ID, 0x00, 0x01, 0, 0, 0, 0, 0]);
|
|
696
|
+
await this.run(() => this.device.sendFeatureReport(DPI_READ_REPORT_ID, req));
|
|
697
|
+
await this.delay(CMD_DELAY_MS);
|
|
698
|
+
const reply = await this.run(() => this.device.receiveFeatureReport(POLLING_REPORT_ID));
|
|
699
|
+
const data = this.copyView(reply);
|
|
700
|
+
const rateByte = data[2]; // byte 2 of feature report (after report ID byte 0)
|
|
701
|
+
const match = POLLING_RATES_1D57.find(([code]) => code === rateByte);
|
|
702
|
+
return match ? match[1] : 0;
|
|
703
|
+
}
|
|
704
|
+
// ── shared helpers ────────────────────────────────────────────────────
|
|
705
|
+
run(task) {
|
|
706
|
+
const next = this.queue.then(task, task);
|
|
707
|
+
this.queue = next.catch(() => undefined);
|
|
708
|
+
return next;
|
|
709
|
+
}
|
|
710
|
+
delay(ms) {
|
|
711
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
712
|
+
}
|
|
713
|
+
copyView(view) {
|
|
714
|
+
return new Uint8Array(view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength));
|
|
715
|
+
}
|
|
716
|
+
/** Check if an input report matches the battery signature. */
|
|
717
|
+
static isBatteryReport(data) {
|
|
718
|
+
return BATTERY_SIGNATURE.every((byte, i) => data[i] === byte);
|
|
719
|
+
}
|
|
720
|
+
/** Extract battery percentage from a battery input report. */
|
|
721
|
+
static parseBatteryReport(data) {
|
|
722
|
+
if (!AttackSharkHidClient.isBatteryReport(data))
|
|
723
|
+
return null;
|
|
724
|
+
const pct = data[4];
|
|
725
|
+
return pct >= 0 && pct <= 100 ? pct : null;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
//# sourceMappingURL=hid.js.map
|