From 6ec5ca135ad621e0db35e3c41bec574d2a613123 Mon Sep 17 00:00:00 2001 From: sakethkanchi Date: Fri, 4 Sep 2026 15:08:20 -0400 Subject: [PATCH 1/4] supported devices: add the VXE R1 on both transports The ATK driver now identifies VXE mice by CID/MID and claims VXE's wired transport, so the R1 is supported over its 2.4 GHz receiver (0x1085) and wired (0xf58f). Both are shared product ids, hence the note pointing at the CID/MID identity rather than the ids. Register VXE as a driver-reported brand alongside CRDRAKO, and pin both product ids in the PID universe: the ATK filter matches on vendor plus usage page, so the protocol pins no ATK product ids of its own. --- src/supported-mice.test.ts | 8 ++++++++ src/supported-mice.ts | 3 +++ 2 files changed, 11 insertions(+) diff --git a/src/supported-mice.test.ts b/src/supported-mice.test.ts index 11b474a4..365e70b7 100644 --- a/src/supported-mice.test.ts +++ b/src/supported-mice.test.ts @@ -57,6 +57,9 @@ const DRIVER_BRANDS = new Set([ // CRDRAKO products are driven by the Lamzu/CompX driver and report their own // brand via deviceBrand(). "crdrako", + // VXE is ATK's sibling brand behind the same vendor id; the ATK driver reports + // it via deviceBrand() once the mouse's CID/MID identifies the model. + "vxe", ]); test("supported / PR / quickwin claims require a registered driver brand", () => { @@ -101,6 +104,11 @@ const PID_UNIVERSE = new Set([ 0x1960, 0x1961, 0x1962, 0x1968, 0x1970, 0x1972, 0x1982, // VGN Dragonfly F2 Master+ (drivers/vgn/hid.ts). 0xfb56, 0xfb57, + // VXE R1, receiver + wired, identified by CID/MID rather than PID + // (drivers/atk/products.ts). The ATK filter matches on vendor + usage page, + // so the protocol pins no ATK product ids of its own; 0xf58f is pinned by + // ATK_COMPX_PRODUCT_IDS because it sits under the shared 0x3554. + 0x1085, 0xf58f, // Pulsar X3 family on the Sonix XS-1 feature interface (drivers/pulsar/pulsar-xs1-hid.ts). ...PULSAR_XS1_PRODUCT_IDS, // Finalmouse ULX dongle (drivers/finalmouse/hid.ts). diff --git a/src/supported-mice.ts b/src/supported-mice.ts index d9101ff4..de42c468 100644 --- a/src/supported-mice.ts +++ b/src/supported-mice.ts @@ -425,6 +425,9 @@ export const MICE: Mouse[] = [ note: "ATK driver (0x373b) likely covers — needs hardware test" }, { brand: "ATK", model: "VXE Zero Normal", status: "likely", req: 1, note: "ATK driver (0x373b) likely covers — needs hardware test" }, + { brand: "VXE", model: "R1", status: "supported", req: 1, + pids: [0x1085, 0xf58f], + note: "ATK driver, both transports verified — receiver 0x1085 and wired 0xf58f are both shared ids, so the mouse is identified by CID/MID 2,12 (PAW3395)" }, { brand: "VGN", model: "Dragonfly F2 Master+", status: "supported", req: 1, pids: [0xfb56, 0xfb57], note: "VGN F2 driver — PIDs 0xfb56/0xfb57" }, From 99b336c97561cd32fc4a3ac9ba352c53076423aa Mon Sep 17 00:00:00 2001 From: logm1lo Date: Sat, 5 Sep 2026 10:51:50 +0700 Subject: [PATCH 2/4] supported-mice: add wired VXE R1 SE+ --- package-lock.json | 3 ++- src/supported-mice.test.ts | 10 +++++++--- src/supported-mice.ts | 6 +++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 12f9e3d5..cface4c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -481,7 +481,8 @@ }, "node_modules/@openmouse/protocol": { "version": "0.1.0", - "resolved": "git+ssh://git@github.com/OpenMouse-Project/mouse-protocol.git#db8fb7deeb5409ffabfa8eb0db087c098cf9a881", + "resolved": "git+https://github.com/OpenMouse-Project/mouse-protocol.git#0b83bf36515ca5f10de644001ae7ca3e464f6f8a", + "integrity": "sha512-TLh2I3wugZ18HCJoOILNtkkSKceAiAtHIow+900J1kD0BOtjvRNcxMSbe9P9LY/aCrKNl++UW9LXhemjQGoTVg==", "engines": { "node": ">=20" } diff --git a/src/supported-mice.test.ts b/src/supported-mice.test.ts index 11b474a4..e49805ab 100644 --- a/src/supported-mice.test.ts +++ b/src/supported-mice.test.ts @@ -2,7 +2,8 @@ import assert from "node:assert/strict"; import test from "node:test"; import { DEVICE_DRIVERS } from "@openmouse/protocol/drivers/registry"; -import { WLMOUSE_PRODUCTS, GLORIOUS_PRODUCTS, GLORIOUS_CLASSIC_PRODUCTS, VENDOR_ID } from "@openmouse/protocol/drivers/vendors"; +import { ATK_COMPX_PRODUCT_IDS } from "@openmouse/protocol/drivers/atk/products"; +import { WLMOUSE_PRODUCTS, GLORIOUS_PRODUCTS, GLORIOUS_CLASSIC_PRODUCTS } from "@openmouse/protocol/drivers/vendors"; import { EGG_DEVICE_PROFILES } from "@openmouse/protocol/endgame-gear-op1"; import { KEYCHRON_NAPE_PRODUCTS } from "@openmouse/protocol/keychron"; import { LAMZU_PRODUCTS } from "@openmouse/protocol/lamzu"; @@ -54,9 +55,10 @@ test("brand + model are unique and request counts are sane", () => { // Brands with an actual driver in @openmouse/protocol/drivers/registry.ts. const DRIVER_BRANDS = new Set([ ...DEVICE_DRIVERS.map((driver) => driver.brand.toLowerCase()), - // CRDRAKO products are driven by the Lamzu/CompX driver and report their own - // brand via deviceBrand(). + // These products are driven by a shared protocol family and report their own + // brand after identification. "crdrako", + "vxe", ]); test("supported / PR / quickwin claims require a registered driver brand", () => { @@ -101,6 +103,8 @@ const PID_UNIVERSE = new Set([ 0x1960, 0x1961, 0x1962, 0x1968, 0x1970, 0x1972, 0x1982, // VGN Dragonfly F2 Master+ (drivers/vgn/hid.ts). 0xfb56, 0xfb57, + // VXE R1 SE+ wired transport (drivers/atk/hid.ts). + ...ATK_COMPX_PRODUCT_IDS, // Pulsar X3 family on the Sonix XS-1 feature interface (drivers/pulsar/pulsar-xs1-hid.ts). ...PULSAR_XS1_PRODUCT_IDS, // Finalmouse ULX dongle (drivers/finalmouse/hid.ts). diff --git a/src/supported-mice.ts b/src/supported-mice.ts index 288ef22b..ee49c1a8 100644 --- a/src/supported-mice.ts +++ b/src/supported-mice.ts @@ -416,9 +416,9 @@ export const MICE: Mouse[] = [ note: "ATK vendor (0x373b, usagePage 0xff02) covered" }, { brand: "ATK", model: "VXE Dragonfly R1 Pro", status: "likely", req: 3, note: "VGN F2 driver (0xfb56/0xfb57) covers Dragonfly F2; R1 Pro needs test" }, - { brand: "ATK", model: "VXE Dragonfly R1 SE / SE+", status: "likely", req: 3, - pids: [0x1085], - note: "Beken R1 on its stock 1K dongle (0x373b:0x1085). Poll rate now reads/writes the 0x0070 live-settings row per OpenVXE; debounce/angle on this family remain unverified" }, + { brand: "VXE", model: "R1 SE+ (wired)", status: "supported", req: 3, + pids: [0xf58f], + note: "Wired 0x3554:0xf58f identity, status reads, and DPI writes verified through Chromium WebHID. Driver supports PAW3395SE DPI, polling, LOD, debounce, motion sync, ripple control, sleep timeout, angle snapping, battery, and firmware; receiver mode remains unverified" }, { brand: "ATK", model: "X1 V2 Ultimate", status: "likely", req: 1, note: "ATK driver (0x373b) likely covers — needs hardware test" }, { brand: "ATK", model: "A9 Air", status: "likely", req: 1, From 1512a4c202df672694ee97a71bc63a34c89b6ac2 Mon Sep 17 00:00:00 2001 From: logm1lo Date: Sat, 5 Sep 2026 19:48:15 +0700 Subject: [PATCH 3/4] control: add complete VXE R1 SE+ support --- package-lock.json | 2 +- src/app/App.tsx | 5 ++ src/app/cards/AdvancedCards.tsx | 50 +++++++---- src/app/cards/AtkCards.tsx | 117 +++++++++++++++++++++++++ src/app/cards/DpiCard.tsx | 13 ++- src/app/cards/availability.test.ts | 38 ++++++++ src/app/cards/availability.ts | 13 ++- src/control-profiles.css | 3 + src/device/atk.test.ts | 17 ++++ src/device/atk.ts | 17 ++++ src/device/controller.ts | 136 ++++++++++++++++++++++++++++- src/device/types.ts | 5 +- src/pending-changes.test.ts | 26 ++++++ src/pending-changes.ts | 4 +- 14 files changed, 421 insertions(+), 25 deletions(-) create mode 100644 src/app/cards/AtkCards.tsx create mode 100644 src/device/atk.test.ts create mode 100644 src/device/atk.ts diff --git a/package-lock.json b/package-lock.json index 352b4365..81dae5eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -481,7 +481,7 @@ }, "node_modules/@openmouse/protocol": { "version": "0.1.0", - "resolved": "git+https://github.com/OpenMouse-Project/mouse-protocol.git#791879a062c5d4f05834045243358bb7e24d44a3", + "resolved": "git+https://github.com/OpenMouse-Project/mouse-protocol.git#2bd619c58c4d0e4a035d1d6714f5c77fad509eca", "engines": { "node": ">=20" } diff --git a/src/app/App.tsx b/src/app/App.tsx index 1a4b7c64..9c97a768 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -18,6 +18,7 @@ import { DpiCard } from "./cards/DpiCard"; import { LightforceCard, PollingCard, SensorCard } from "./cards/PerformanceCards"; import { LightingCard } from "./cards/LightingCard"; import { MxMasterButtonsCard, MxMasterCards } from "./cards/MxMasterCards"; +import { AtkButtonCard, AtkProfileCard, AtkReceiverCard } from "./cards/AtkCards"; import { DebounceCard, EggButtonCard, @@ -144,6 +145,9 @@ function Workspace({ show(has.razerButtons, ["buttons"]) ? : null, show(has.mxMasterButtons, ["buttons"]) ? : null, + show(has.atkButtons, ["buttons"]) ? : null, + show(has.atkProfile, ["profiles"]) ? : null, + show(has.atkReceiver, ["advanced"]) ? : null, show(has.pulsarPro, ["profiles"]) ? : null, ].filter((node) => node !== null); @@ -290,6 +294,7 @@ export function App(): ReactNode { if(!has.eggButtons&& !has.razerButtons&& !has.mxMasterButtons&& + !has.atkButtons&& !has.debounce&& !has.lightforce&& !has.eggSpdt&& diff --git a/src/app/cards/AdvancedCards.tsx b/src/app/cards/AdvancedCards.tsx index 13862fda..cfd37ce3 100644 --- a/src/app/cards/AdvancedCards.tsx +++ b/src/app/cards/AdvancedCards.tsx @@ -131,6 +131,10 @@ export function DebounceCard({ snapshot }: { snapshot: ControlSnapshot }): React const max = snapshot.traits.directMode ? snapshot.capabilities?.debounceMaxMs ?? 20 : snapshot.capabilities?.teevolutionProfile?.debounce.max ?? 20; + const offered = snapshot.capabilities?.debounceOptions; + const options = offered + ? selectableValues([...offered], status.debounceMs) ?? offered + : Array.from({ length: max + 1 }, (_, ms) => ms); const staged = snapshot.pending.keys.includes("debounce"); return (
@@ -141,7 +145,7 @@ export function DebounceCard({ snapshot }: { snapshot: ControlSnapshot }): React disabled={status.debounceMs === null || status.debounceMs === undefined} onChange={(event) => control.applyPulsarValue("debounce", Number(event.currentTarget.value))} > - {Array.from({ length: max + 1 }, (_, ms) => )} + {options.map((ms) => )}
); @@ -263,6 +267,7 @@ export function ProcessingCard({ snapshot }: { snapshot: ControlSnapshot }): Rea const performanceLabel = status.brand === "CRDRAKO" ? "Competitive mode" : status.brand === "Teevolution" ? "Highest performance" : "Performance mode"; + const angleSnappingLabel = status.ui?.family === "atk" ? "Straight-line correction" : "Angle snapping"; return (
@@ -300,7 +305,7 @@ export function ProcessingCard({ snapshot }: { snapshot: ControlSnapshot }): Rea />