yjx-sdk-mouse 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/README.md +287 -0
- package/dist/constants.d.ts +79 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +100 -0
- package/dist/constants.js.map +1 -0
- package/dist/controllers/button.d.ts +15 -0
- package/dist/controllers/button.d.ts.map +1 -0
- package/dist/controllers/button.js +40 -0
- package/dist/controllers/button.js.map +1 -0
- package/dist/controllers/device.d.ts +19 -0
- package/dist/controllers/device.d.ts.map +1 -0
- package/dist/controllers/device.js +111 -0
- package/dist/controllers/device.js.map +1 -0
- package/dist/controllers/dpi.d.ts +15 -0
- package/dist/controllers/dpi.d.ts.map +1 -0
- package/dist/controllers/dpi.js +40 -0
- package/dist/controllers/dpi.js.map +1 -0
- package/dist/controllers/macro.d.ts +18 -0
- package/dist/controllers/macro.d.ts.map +1 -0
- package/dist/controllers/macro.js +31 -0
- package/dist/controllers/macro.js.map +1 -0
- package/dist/controllers/other.d.ts +17 -0
- package/dist/controllers/other.d.ts.map +1 -0
- package/dist/controllers/other.js +29 -0
- package/dist/controllers/other.js.map +1 -0
- package/dist/controllers/performance.d.ts +10 -0
- package/dist/controllers/performance.d.ts.map +1 -0
- package/dist/controllers/performance.js +28 -0
- package/dist/controllers/performance.js.map +1 -0
- package/dist/device/index.d.ts +57 -0
- package/dist/device/index.d.ts.map +1 -0
- package/dist/device/index.js +341 -0
- package/dist/device/index.js.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +102 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/command-presets.d.ts +11 -0
- package/dist/protocol/command-presets.d.ts.map +1 -0
- package/dist/protocol/command-presets.js +89 -0
- package/dist/protocol/command-presets.js.map +1 -0
- package/dist/protocol/key.d.ts +20 -0
- package/dist/protocol/key.d.ts.map +1 -0
- package/dist/protocol/key.js +197 -0
- package/dist/protocol/key.js.map +1 -0
- package/dist/protocol/keyboard-presets.d.ts +6 -0
- package/dist/protocol/keyboard-presets.d.ts.map +1 -0
- package/dist/protocol/keyboard-presets.js +211 -0
- package/dist/protocol/keyboard-presets.js.map +1 -0
- package/dist/protocol/macro.d.ts +23 -0
- package/dist/protocol/macro.d.ts.map +1 -0
- package/dist/protocol/macro.js +144 -0
- package/dist/protocol/macro.js.map +1 -0
- package/dist/protocol/packet.d.ts +88 -0
- package/dist/protocol/packet.d.ts.map +1 -0
- package/dist/protocol/packet.js +294 -0
- package/dist/protocol/packet.js.map +1 -0
- package/dist/types.d.ts +127 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/webhid.d.ts +51 -0
- package/dist/webhid.d.ts.map +1 -0
- package/dist/webhid.js +2 -0
- package/dist/webhid.js.map +1 -0
- package/package.json +38 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.js","sourceRoot":"","sources":["../../src/controllers/device.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,uBAAuB,EACvB,gBAAgB,EAChB,4BAA4B,EAC5B,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,sBAAsB,EACtB,KAAK,GAEN,MAAM,uBAAuB,CAAC;AAI/B,MAAM,cAAc,GAAG,CAAC,CAAC;AAEzB,SAAS,eAAe,CAAC,MAAgB,EAAE,IAAe;IACxD,MAAM,UAAU,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;IAC/B,OAAO,UAAU,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;QAC1C,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,OAAO,gBAAgB;IAGE;IAFrB,qBAAqB,GAAkB,IAAI,CAAC;IAEpD,YAA6B,MAAmB;QAAnB,WAAM,GAAN,MAAM,CAAa;IAAG,CAAC;IAEpD,KAAK,CAAC,SAAS;QACb,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,qBAAqB,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,iDAAiD;IACjD,KAAK,CAAC,kBAAkB,CAAC,OAAO,GAAG,KAAK;QACtC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAC3C,OAAO,IAAI,CAAC,qBAAqB,CAAC;QACpC,CAAC;QAED,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,IAAI,EAAE,EAAE,CAChE,sBAAsB,CAAC,IAAI,CAAC,CAC7B,CAAC;QACF,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,gBAAgB,EAAE,CAAC,CAAC;QACrD,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC;QAC9B,CAAC;QACD,IAAI,CAAC,qBAAqB,GAAG,OAAO,CAAC;QACrC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAExD,IAAI,OAA2B,CAAC;QAChC,IAAI,GAAG,GAAe,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;QACxC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,oBAAoB,EAAE,CAAC,CAAC;YACvE,GAAG,GAAG,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC;YAC/B,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,4BAA4B;QAC9B,CAAC;QAED,OAAO;YACL,GAAG;YACH,OAAO;YACP,eAAe;YACf,uBAAuB,EAAE,eAAe;SACzC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,iBAAiB,EAAE,CAAC,CAAC;QACpE,OAAO,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,MAAuB;QAC1C,MAAM,WAAW,GAAG,kBAAkB,CAAC,4BAA4B,CAAC,MAAM,CAAC,CAAC,CAAC;QAC7E,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;QAEtE,IAAI,aAAa,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CACb,+BAA+B,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,SAAS,KAAK,CAAC,aAAa,EAAE,EAAE,CAAC,EAAE,CACvG,CAAC;QACJ,CAAC;QAED,OAAO,gBAAgB,CAAC,aAAa,CAAC,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAE3C,OAAO;YACL,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,QAAQ,EAAE,iBAAiB,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,IAAI;YACzD,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;SACpE,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,OAAmB;QACrC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC/D,MAAM,MAAM,GAAG,uBAAuB,CACpC;YACE,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,MAAM;SACP,EACD,IAAI,CACL,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QACjD,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QACjF,MAAM,UAAU,GAAG,MAAM;aACtB,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;aACtF,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC;QAEnD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,UAAU;iBACtB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,KAAK,GAAG,CAAC,QAAQ,IAAI,CAAC,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;iBAC5E,IAAI,CAAC,IAAI,CAAC,CAAC;YACd,MAAM,IAAI,KAAK,CACb,iBAAiB,MAAM,EAAE,CAC1B,CAAC;QACJ,CAAC;QAED,OAAO;YACL,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,QAAQ,EAAE,iBAAiB,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,OAAO,CAAC,QAAQ;YACrE,MAAM,EAAE,YAAY;SACrB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;QACxB,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DeviceController } from './device.js';
|
|
2
|
+
import type { DpiProfile } from '../types.js';
|
|
3
|
+
export declare class DpiController {
|
|
4
|
+
private readonly deviceController;
|
|
5
|
+
constructor(deviceController: DeviceController);
|
|
6
|
+
get(): Promise<DpiProfile>;
|
|
7
|
+
set(profile: DpiProfile): Promise<DpiProfile>;
|
|
8
|
+
setCurrentIndex(index: number): Promise<DpiProfile>;
|
|
9
|
+
/** 使用本地 profile 切换档位,避免 get() 覆盖未写入的修改 */
|
|
10
|
+
setCurrentIndexFrom(profile: DpiProfile, index: number): Promise<DpiProfile>;
|
|
11
|
+
setLevel(slotIndex: number, dpi: number): Promise<DpiProfile>;
|
|
12
|
+
/** 修改单档 DPI 并写入(保留当前 profile 其它字段) */
|
|
13
|
+
setLevelFrom(profile: DpiProfile, slotIndex: number, dpi: number): Promise<DpiProfile>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=dpi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dpi.d.ts","sourceRoot":"","sources":["../../src/controllers/dpi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,qBAAa,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBAAhB,gBAAgB,EAAE,gBAAgB;IAEzD,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC;IAI1B,GAAG,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAI7C,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAKzD,0CAA0C;IACpC,mBAAmB,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAI5E,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAUnE,sCAAsC;IAChC,YAAY,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;CAS7F"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export class DpiController {
|
|
2
|
+
deviceController;
|
|
3
|
+
constructor(deviceController) {
|
|
4
|
+
this.deviceController = deviceController;
|
|
5
|
+
}
|
|
6
|
+
async get() {
|
|
7
|
+
return this.deviceController.getDpiProfile();
|
|
8
|
+
}
|
|
9
|
+
async set(profile) {
|
|
10
|
+
return this.deviceController.setDpiProfile(profile);
|
|
11
|
+
}
|
|
12
|
+
async setCurrentIndex(index) {
|
|
13
|
+
const current = await this.get();
|
|
14
|
+
return this.set({ ...current, currentIndex: index });
|
|
15
|
+
}
|
|
16
|
+
/** 使用本地 profile 切换档位,避免 get() 覆盖未写入的修改 */
|
|
17
|
+
async setCurrentIndexFrom(profile, index) {
|
|
18
|
+
return this.set({ ...profile, currentIndex: index });
|
|
19
|
+
}
|
|
20
|
+
async setLevel(slotIndex, dpi) {
|
|
21
|
+
const current = await this.get();
|
|
22
|
+
const levels = [...current.levels];
|
|
23
|
+
while (levels.length <= slotIndex) {
|
|
24
|
+
levels.push(800);
|
|
25
|
+
}
|
|
26
|
+
levels[slotIndex] = dpi;
|
|
27
|
+
return this.set({ ...current, levels });
|
|
28
|
+
}
|
|
29
|
+
/** 修改单档 DPI 并写入(保留当前 profile 其它字段) */
|
|
30
|
+
async setLevelFrom(profile, slotIndex, dpi) {
|
|
31
|
+
const levels = [...profile.levels];
|
|
32
|
+
while (levels.length <= slotIndex) {
|
|
33
|
+
levels.push(800);
|
|
34
|
+
}
|
|
35
|
+
levels[slotIndex] = dpi;
|
|
36
|
+
// 原厂改 DPI 时 packet[12]=dpi_index+1 指向当前编辑档位(抓包验证)
|
|
37
|
+
return this.set({ ...profile, levels, currentIndex: slotIndex });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=dpi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dpi.js","sourceRoot":"","sources":["../../src/controllers/dpi.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,aAAa;IACK;IAA7B,YAA6B,gBAAkC;QAAlC,qBAAgB,GAAhB,gBAAgB,CAAkB;IAAG,CAAC;IAEnE,KAAK,CAAC,GAAG;QACP,OAAO,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,OAAmB;QAC3B,OAAO,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,KAAa;QACjC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,0CAA0C;IAC1C,KAAK,CAAC,mBAAmB,CAAC,OAAmB,EAAE,KAAa;QAC1D,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,SAAiB,EAAE,GAAW;QAC3C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QACnC,OAAO,MAAM,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;QACD,MAAM,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC;QACxB,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED,sCAAsC;IACtC,KAAK,CAAC,YAAY,CAAC,OAAmB,EAAE,SAAiB,EAAE,GAAW;QACpE,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QACnC,OAAO,MAAM,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;QACD,MAAM,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC;QACxB,kDAAkD;QAClD,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC,CAAC;IACnE,CAAC;CACF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ButtonController } from './button.js';
|
|
2
|
+
import type { DeviceLayer } from '../device/index.js';
|
|
3
|
+
import type { ButtonMapping, MacroButtonOptions, MacroProfileIndex, MouseButtonSlot, MouseMacro } from '../types.js';
|
|
4
|
+
export declare class MacroController {
|
|
5
|
+
private readonly device;
|
|
6
|
+
private readonly button;
|
|
7
|
+
constructor(device: DeviceLayer, button: ButtonController);
|
|
8
|
+
/**
|
|
9
|
+
* 将宏列表写入指定 profile。
|
|
10
|
+
* 数组索引对应宏索引(0..31),空 actions 可用于保留空宏槽位。
|
|
11
|
+
*/
|
|
12
|
+
set(macros: readonly MouseMacro[], profileIndex?: MacroProfileIndex): Promise<void>;
|
|
13
|
+
/** 将已经写入设备的宏分配给指定鼠标按键。 */
|
|
14
|
+
assignButton(slot: MouseButtonSlot, options: MacroButtonOptions): Promise<ButtonMapping>;
|
|
15
|
+
/** 写入宏数据,然后将其中一个宏分配给指定鼠标按键。 */
|
|
16
|
+
setAndAssign(slot: MouseButtonSlot, macros: readonly MouseMacro[], options: MacroButtonOptions, profileIndex?: MacroProfileIndex): Promise<ButtonMapping>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=macro.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"macro.d.ts","sourceRoot":"","sources":["../../src/controllers/macro.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAM/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EACV,aAAa,EACb,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,UAAU,EACX,MAAM,aAAa,CAAC;AAErB,qBAAa,eAAe;IAExB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,gBAAgB;IAG3C;;;OAGG;IACG,GAAG,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,EAAE,YAAY,GAAE,iBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAO5F,0BAA0B;IACpB,YAAY,CAChB,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,aAAa,CAAC;IAMzB,+BAA+B;IACzB,YAAY,CAChB,IAAI,EAAE,eAAe,EACrB,MAAM,EAAE,SAAS,UAAU,EAAE,EAC7B,OAAO,EAAE,kBAAkB,EAC3B,YAAY,GAAE,iBAAqB,GAClC,OAAO,CAAC,aAAa,CAAC;CAI1B"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { buildMacroWritePackets, createMacroCommitPacket, macroAssignmentToKey, } from '../protocol/macro.js';
|
|
2
|
+
export class MacroController {
|
|
3
|
+
device;
|
|
4
|
+
button;
|
|
5
|
+
constructor(device, button) {
|
|
6
|
+
this.device = device;
|
|
7
|
+
this.button = button;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 将宏列表写入指定 profile。
|
|
11
|
+
* 数组索引对应宏索引(0..31),空 actions 可用于保留空宏槽位。
|
|
12
|
+
*/
|
|
13
|
+
async set(macros, profileIndex = 0) {
|
|
14
|
+
for (const packet of buildMacroWritePackets(macros, profileIndex)) {
|
|
15
|
+
await this.device.sendNoResponse(packet);
|
|
16
|
+
}
|
|
17
|
+
await this.device.sendNoResponse(createMacroCommitPacket());
|
|
18
|
+
}
|
|
19
|
+
/** 将已经写入设备的宏分配给指定鼠标按键。 */
|
|
20
|
+
async assignButton(slot, options) {
|
|
21
|
+
const current = await this.button.get();
|
|
22
|
+
const key = macroAssignmentToKey(current[slot].index, options);
|
|
23
|
+
return this.button.set({ ...current, [slot]: key });
|
|
24
|
+
}
|
|
25
|
+
/** 写入宏数据,然后将其中一个宏分配给指定鼠标按键。 */
|
|
26
|
+
async setAndAssign(slot, macros, options, profileIndex = 0) {
|
|
27
|
+
await this.set(macros, profileIndex);
|
|
28
|
+
return this.assignButton(slot, options);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=macro.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"macro.js","sourceRoot":"","sources":["../../src/controllers/macro.ts"],"names":[],"mappings":"AACA,OAAO,EACL,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAU9B,MAAM,OAAO,eAAe;IAEP;IACA;IAFnB,YACmB,MAAmB,EACnB,MAAwB;QADxB,WAAM,GAAN,MAAM,CAAa;QACnB,WAAM,GAAN,MAAM,CAAkB;IACxC,CAAC;IAEJ;;;OAGG;IACH,KAAK,CAAC,GAAG,CAAC,MAA6B,EAAE,eAAkC,CAAC;QAC1E,KAAK,MAAM,MAAM,IAAI,sBAAsB,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC;YAClE,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAC3C,CAAC;QACD,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,uBAAuB,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,0BAA0B;IAC1B,KAAK,CAAC,YAAY,CAChB,IAAqB,EACrB,OAA2B;QAE3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,oBAAoB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC/D,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,+BAA+B;IAC/B,KAAK,CAAC,YAAY,CAChB,IAAqB,EACrB,MAA6B,EAC7B,OAA2B,EAC3B,eAAkC,CAAC;QAEnC,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACrC,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ButtonController } from './button.js';
|
|
2
|
+
import type { DeviceController } from './device.js';
|
|
3
|
+
import type { DeviceLayer } from '../device/index.js';
|
|
4
|
+
import type { ButtonMapping, DpiProfile, PerformanceSettings } from '../types.js';
|
|
5
|
+
export interface FactoryResetResult {
|
|
6
|
+
performance: PerformanceSettings;
|
|
7
|
+
dpi: DpiProfile;
|
|
8
|
+
buttons: ButtonMapping;
|
|
9
|
+
}
|
|
10
|
+
export declare class OtherController {
|
|
11
|
+
private readonly deviceLayer;
|
|
12
|
+
private readonly deviceController;
|
|
13
|
+
private readonly button;
|
|
14
|
+
constructor(deviceLayer: DeviceLayer, deviceController: DeviceController, button: ButtonController);
|
|
15
|
+
factoryReset(): Promise<FactoryResetResult>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=other.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"other.d.ts","sourceRoot":"","sources":["../../src/controllers/other.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElF,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,mBAAmB,CAAC;IACjC,GAAG,EAAE,UAAU,CAAC;IAChB,OAAO,EAAE,aAAa,CAAC;CACxB;AAED,qBAAa,eAAe;IAExB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAFN,WAAW,EAAE,WAAW,EACxB,gBAAgB,EAAE,gBAAgB,EAClC,MAAM,EAAE,gBAAgB;IAGrC,YAAY,IAAI,OAAO,CAAC,kBAAkB,CAAC;CAkBlD"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createFactoryDefaultMouseConfig, mouseConfigToDpiProfile, mouseConfigToPerformanceSettings, } from '../protocol/packet.js';
|
|
2
|
+
import { createMacroResetPacket } from '../protocol/macro.js';
|
|
3
|
+
export class OtherController {
|
|
4
|
+
deviceLayer;
|
|
5
|
+
deviceController;
|
|
6
|
+
button;
|
|
7
|
+
constructor(deviceLayer, deviceController, button) {
|
|
8
|
+
this.deviceLayer = deviceLayer;
|
|
9
|
+
this.deviceController = deviceController;
|
|
10
|
+
this.button = button;
|
|
11
|
+
}
|
|
12
|
+
async factoryReset() {
|
|
13
|
+
const defaultConfig = createFactoryDefaultMouseConfig();
|
|
14
|
+
const written = await this.deviceController.setMouseConfig(defaultConfig);
|
|
15
|
+
const buttons = await this.button.resetAll();
|
|
16
|
+
await this.deviceLayer.sendNoResponse(createMacroResetPacket());
|
|
17
|
+
const dpiProfile = mouseConfigToDpiProfile(written);
|
|
18
|
+
return {
|
|
19
|
+
performance: mouseConfigToPerformanceSettings(written),
|
|
20
|
+
dpi: {
|
|
21
|
+
currentIndex: dpiProfile.currentIndex,
|
|
22
|
+
pollRate: mouseConfigToPerformanceSettings(written).pollRate,
|
|
23
|
+
levels: dpiProfile.dpiLevels,
|
|
24
|
+
},
|
|
25
|
+
buttons,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=other.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"other.js","sourceRoot":"","sources":["../../src/controllers/other.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,+BAA+B,EAC/B,uBAAuB,EACvB,gCAAgC,GACjC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAY9D,MAAM,OAAO,eAAe;IAEP;IACA;IACA;IAHnB,YACmB,WAAwB,EACxB,gBAAkC,EAClC,MAAwB;QAFxB,gBAAW,GAAX,WAAW,CAAa;QACxB,qBAAgB,GAAhB,gBAAgB,CAAkB;QAClC,WAAM,GAAN,MAAM,CAAkB;IACxC,CAAC;IAEJ,KAAK,CAAC,YAAY;QAChB,MAAM,aAAa,GAAG,+BAA+B,EAAE,CAAC;QACxD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;QAC1E,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QAE7C,MAAM,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,sBAAsB,EAAE,CAAC,CAAC;QAEhE,MAAM,UAAU,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;QACpD,OAAO;YACL,WAAW,EAAE,gCAAgC,CAAC,OAAO,CAAC;YACtD,GAAG,EAAE;gBACH,YAAY,EAAE,UAAU,CAAC,YAAY;gBACrC,QAAQ,EAAE,gCAAgC,CAAC,OAAO,CAAC,CAAC,QAAQ;gBAC5D,MAAM,EAAE,UAAU,CAAC,SAAS;aAC7B;YACD,OAAO;SACR,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DeviceController } from './device.js';
|
|
2
|
+
import type { DpiProfile, PerformanceSettings } from '../types.js';
|
|
3
|
+
export declare class PerformanceController {
|
|
4
|
+
private readonly deviceController;
|
|
5
|
+
constructor(deviceController: DeviceController);
|
|
6
|
+
get(): Promise<PerformanceSettings>;
|
|
7
|
+
set(settings: Partial<PerformanceSettings>): Promise<PerformanceSettings>;
|
|
8
|
+
setPollRate(pollRate: 125 | 250 | 500 | 1000): Promise<DpiProfile>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=performance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"performance.d.ts","sourceRoot":"","sources":["../../src/controllers/performance.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEnE,qBAAa,qBAAqB;IACpB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBAAhB,gBAAgB,EAAE,gBAAgB;IAEzD,GAAG,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAKnC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAczE,WAAW,CAAC,QAAQ,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC;CAIzE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { PollRateToIndex } from '../constants.js';
|
|
2
|
+
import { applyPerformanceSettingsToMouseConfig, mouseConfigToPerformanceSettings, } from '../protocol/packet.js';
|
|
3
|
+
export class PerformanceController {
|
|
4
|
+
deviceController;
|
|
5
|
+
constructor(deviceController) {
|
|
6
|
+
this.deviceController = deviceController;
|
|
7
|
+
}
|
|
8
|
+
async get() {
|
|
9
|
+
const config = await this.deviceController.getMouseConfig();
|
|
10
|
+
return mouseConfigToPerformanceSettings(config);
|
|
11
|
+
}
|
|
12
|
+
async set(settings) {
|
|
13
|
+
const base = await this.deviceController.getMouseConfig();
|
|
14
|
+
const current = mouseConfigToPerformanceSettings(base);
|
|
15
|
+
const next = { ...current, ...settings };
|
|
16
|
+
if (PollRateToIndex[next.pollRate] === undefined) {
|
|
17
|
+
throw new Error(`不支持的回报率: ${next.pollRate}`);
|
|
18
|
+
}
|
|
19
|
+
const config = applyPerformanceSettingsToMouseConfig(base, next);
|
|
20
|
+
const written = await this.deviceController.setMouseConfig(config);
|
|
21
|
+
return mouseConfigToPerformanceSettings(written);
|
|
22
|
+
}
|
|
23
|
+
async setPollRate(pollRate) {
|
|
24
|
+
await this.set({ pollRate });
|
|
25
|
+
return this.deviceController.getDpiProfile();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=performance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"performance.js","sourceRoot":"","sources":["../../src/controllers/performance.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EACL,qCAAqC,EACrC,gCAAgC,GACjC,MAAM,uBAAuB,CAAC;AAI/B,MAAM,OAAO,qBAAqB;IACH;IAA7B,YAA6B,gBAAkC;QAAlC,qBAAgB,GAAhB,gBAAgB,CAAkB;IAAG,CAAC;IAEnE,KAAK,CAAC,GAAG;QACP,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,cAAc,EAAE,CAAC;QAC5D,OAAO,gCAAgC,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,QAAsC;QAC9C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,cAAc,EAAE,CAAC;QAC1D,MAAM,OAAO,GAAG,gCAAgC,CAAC,IAAI,CAAC,CAAC;QACvD,MAAM,IAAI,GAAwB,EAAE,GAAG,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC;QAE9D,IAAI,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,SAAS,EAAE,CAAC;YACjD,MAAM,IAAI,KAAK,CAAC,YAAY,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,MAAM,GAAG,qCAAqC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACjE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QACnE,OAAO,gCAAgC,CAAC,OAAO,CAAC,CAAC;IACnD,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,QAAgC;QAChD,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,CAAC;IAC/C,CAAC;CACF"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { DeviceFilterConfig, MouseDeviceInfo, MouseStatusEvent, UsbChangeEvent } from '../types.js';
|
|
2
|
+
export interface DeviceLayerOptions {
|
|
3
|
+
configs: DeviceFilterConfig[];
|
|
4
|
+
usagePage?: number;
|
|
5
|
+
usage?: number;
|
|
6
|
+
timeout?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class DeviceLayer {
|
|
9
|
+
private readonly configs;
|
|
10
|
+
private readonly timeout;
|
|
11
|
+
private readonly listeners;
|
|
12
|
+
private device;
|
|
13
|
+
private readonly inputHandler;
|
|
14
|
+
private queue;
|
|
15
|
+
private flushing;
|
|
16
|
+
private pending;
|
|
17
|
+
private reportWaiters;
|
|
18
|
+
constructor(options: DeviceLayerOptions);
|
|
19
|
+
private bindNavigatorEvents;
|
|
20
|
+
private matchDevice;
|
|
21
|
+
private matchesTargetCollection;
|
|
22
|
+
private getFilters;
|
|
23
|
+
private formatSendError;
|
|
24
|
+
private toDeviceInfo;
|
|
25
|
+
private getDeviceId;
|
|
26
|
+
on(eventName: 'usbChange' | 'statusChange', handler: (event: UsbChangeEvent | MouseStatusEvent) => void): void;
|
|
27
|
+
off(eventName: 'usbChange' | 'statusChange', handler?: (event: UsbChangeEvent | MouseStatusEvent) => void): void;
|
|
28
|
+
private emit;
|
|
29
|
+
getDevices(): Promise<MouseDeviceInfo[]>;
|
|
30
|
+
requestDevices(): Promise<MouseDeviceInfo[]>;
|
|
31
|
+
init(id: string): Promise<MouseDeviceInfo>;
|
|
32
|
+
private attachDevice;
|
|
33
|
+
private detachDevice;
|
|
34
|
+
closeDevice(): Promise<void>;
|
|
35
|
+
get opened(): boolean;
|
|
36
|
+
get currentDevice(): MouseDeviceInfo | null;
|
|
37
|
+
private onInputReport;
|
|
38
|
+
sendAndWait(packet: Uint8Array, timeout?: number): Promise<Uint8Array>;
|
|
39
|
+
/**
|
|
40
|
+
* 原厂 webhid_write_command:写入后读取下一帧响应(命令字可能与请求不同)
|
|
41
|
+
*/
|
|
42
|
+
sendWriteAndWait(packet: Uint8Array, timeout?: number): Promise<Uint8Array>;
|
|
43
|
+
private enqueue;
|
|
44
|
+
sendNoResponse(packet: Uint8Array): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* 等待设备主动上报并解析(如 Ping 触发的固件版本包)。
|
|
47
|
+
* 对齐原厂 getVersionInfo:先发 Ping,再监听 cmd=0x04 异步回包。
|
|
48
|
+
*/
|
|
49
|
+
waitForMatchingReport(matcher: (data: Uint8Array) => string | null | undefined, timeout?: number): Promise<string>;
|
|
50
|
+
private notifyReportWaiters;
|
|
51
|
+
private waitForIdle;
|
|
52
|
+
private flushQueue;
|
|
53
|
+
private rejectAll;
|
|
54
|
+
}
|
|
55
|
+
export declare function createDefaultConfigs(): DeviceFilterConfig[];
|
|
56
|
+
export declare function createDefaultConfig(): DeviceFilterConfig;
|
|
57
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/device/index.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EACV,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACf,MAAM,aAAa,CAAC;AAwBrB,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoD;IAC9E,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6D;IAC1F,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,aAAa,CAAsB;gBAE/B,OAAO,EAAE,kBAAkB;IAMvC,OAAO,CAAC,mBAAmB;IAsB3B,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,WAAW;IAInB,EAAE,CACA,SAAS,EAAE,WAAW,GAAG,cAAc,EACvC,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,GAAG,gBAAgB,KAAK,IAAI,GAC1D,IAAI;IAMP,GAAG,CACD,SAAS,EAAE,WAAW,GAAG,cAAc,EACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,GAAG,gBAAgB,KAAK,IAAI,GAC3D,IAAI;IAYP,OAAO,CAAC,IAAI;IAkBN,UAAU,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IASxC,cAAc,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAiB5C,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAwBhD,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,YAAY;IASd,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAQlC,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,IAAI,aAAa,IAAI,eAAe,GAAG,IAAI,CAE1C;IAED,OAAO,CAAC,aAAa;IAoCf,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,SAAe,GAAG,OAAO,CAAC,UAAU,CAAC;IAIlF;;OAEG;IACG,gBAAgB,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,SAAe,GAAG,OAAO,CAAC,UAAU,CAAC;IAIvF,OAAO,CAAC,OAAO;IAkCT,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IASvD;;;OAGG;IACH,qBAAqB,CACnB,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,EACxD,OAAO,SAAe,GACrB,OAAO,CAAC,MAAM,CAAC;IAWlB,OAAO,CAAC,mBAAmB;YAab,WAAW;YAMX,UAAU;IA0BxB,OAAO,CAAC,SAAS;CAmBlB;AAED,wBAAgB,oBAAoB,IAAI,kBAAkB,EAAE,CAe3D;AAED,wBAAgB,mBAAmB,IAAI,kBAAkB,CAExD"}
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { DEFAULT_PRODUCT_ID, DEFAULT_USAGE, DEFAULT_USAGE_PAGE, RECEIVER_VENDOR_ID, WIRED_VENDOR_ID, REPORT_ID, } from '../constants.js';
|
|
2
|
+
import { PollRateFromIndex } from '../constants.js';
|
|
3
|
+
import { getResponseCommand, isValidResponse, normalizeInputReport, parseStatusReport, toPayloadBuffer, } from '../protocol/packet.js';
|
|
4
|
+
/** 写配置/按键后设备常见回包命令字(0x0F 写配置会回显 0x0F,见 Bus Hound 抓包) */
|
|
5
|
+
const WRITE_RESPONSE_COMMANDS = new Set([0x0f, 0x0e, 0x09, 0x08]);
|
|
6
|
+
export class DeviceLayer {
|
|
7
|
+
configs;
|
|
8
|
+
timeout;
|
|
9
|
+
listeners = new Map();
|
|
10
|
+
device = null;
|
|
11
|
+
inputHandler = (event) => this.onInputReport(event);
|
|
12
|
+
queue = [];
|
|
13
|
+
flushing = false;
|
|
14
|
+
pending = null;
|
|
15
|
+
reportWaiters = [];
|
|
16
|
+
constructor(options) {
|
|
17
|
+
this.configs = options.configs;
|
|
18
|
+
this.timeout = options.timeout ?? 3000;
|
|
19
|
+
this.bindNavigatorEvents();
|
|
20
|
+
}
|
|
21
|
+
bindNavigatorEvents() {
|
|
22
|
+
if (!navigator.hid) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
navigator.hid.addEventListener('connect', (event) => {
|
|
26
|
+
if (this.matchDevice(event.device)) {
|
|
27
|
+
this.emit('usbChange', { type: 'connect', device: event.device });
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
navigator.hid.addEventListener('disconnect', (event) => {
|
|
31
|
+
if (this.device && event.device === this.device) {
|
|
32
|
+
this.rejectAll(new Error('设备已断开'));
|
|
33
|
+
this.detachDevice();
|
|
34
|
+
this.emit('usbChange', { type: 'disconnect', device: event.device });
|
|
35
|
+
}
|
|
36
|
+
else if (this.matchDevice(event.device)) {
|
|
37
|
+
this.emit('usbChange', { type: 'disconnect', device: event.device });
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
matchDevice(device) {
|
|
42
|
+
return this.configs.some((config) => device.vendorId === config.vendorId &&
|
|
43
|
+
device.productId === config.productId &&
|
|
44
|
+
this.matchesTargetCollection(device));
|
|
45
|
+
}
|
|
46
|
+
matchesTargetCollection(device) {
|
|
47
|
+
return this.configs.some((config) => device.collections.some((collection) => collection.usagePage === config.usagePage && collection.usage === config.usage));
|
|
48
|
+
}
|
|
49
|
+
getFilters() {
|
|
50
|
+
return this.configs.map((config) => ({
|
|
51
|
+
vendorId: config.vendorId,
|
|
52
|
+
productId: config.productId,
|
|
53
|
+
usagePage: config.usagePage,
|
|
54
|
+
usage: config.usage,
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
formatSendError(error) {
|
|
58
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
59
|
+
if (message.includes('Failed to write the report')) {
|
|
60
|
+
return new Error('HID 写入失败:请关闭原厂驱动后重试。Web HID 需使用 Report ID 0xF0 发送(已自动处理)');
|
|
61
|
+
}
|
|
62
|
+
if (message.includes('Failed to open')) {
|
|
63
|
+
return new Error('无法打开设备:设备可能被其他程序占用,请先关闭原厂驱动');
|
|
64
|
+
}
|
|
65
|
+
return error instanceof Error ? error : new Error(message);
|
|
66
|
+
}
|
|
67
|
+
toDeviceInfo(device) {
|
|
68
|
+
return {
|
|
69
|
+
id: this.getDeviceId(device),
|
|
70
|
+
vendorId: device.vendorId,
|
|
71
|
+
productId: device.productId,
|
|
72
|
+
productName: device.productName ?? 'Unknown',
|
|
73
|
+
opened: device.opened,
|
|
74
|
+
raw: device,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
getDeviceId(device) {
|
|
78
|
+
return `${device.vendorId.toString(16)}-${device.productId.toString(16)}-${device.productName ?? 'device'}`;
|
|
79
|
+
}
|
|
80
|
+
on(eventName, handler) {
|
|
81
|
+
const handlers = this.listeners.get(eventName) ?? new Set();
|
|
82
|
+
handlers.add(handler);
|
|
83
|
+
this.listeners.set(eventName, handlers);
|
|
84
|
+
}
|
|
85
|
+
off(eventName, handler) {
|
|
86
|
+
const handlers = this.listeners.get(eventName);
|
|
87
|
+
if (!handlers) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (handler) {
|
|
91
|
+
handlers.delete(handler);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
handlers.clear();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
emit(eventName, payload) {
|
|
98
|
+
const handlers = this.listeners.get(eventName);
|
|
99
|
+
if (!handlers) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (eventName === 'usbChange') {
|
|
103
|
+
for (const handler of handlers) {
|
|
104
|
+
handler(payload);
|
|
105
|
+
}
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
for (const handler of handlers) {
|
|
109
|
+
handler(payload);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async getDevices() {
|
|
113
|
+
if (!navigator.hid) {
|
|
114
|
+
throw new Error('当前环境不支持 Web HID API');
|
|
115
|
+
}
|
|
116
|
+
const authorized = await navigator.hid.getDevices();
|
|
117
|
+
return authorized.filter((device) => this.matchDevice(device)).map((device) => this.toDeviceInfo(device));
|
|
118
|
+
}
|
|
119
|
+
async requestDevices() {
|
|
120
|
+
if (!navigator.hid) {
|
|
121
|
+
throw new Error('当前环境不支持 Web HID API');
|
|
122
|
+
}
|
|
123
|
+
const devices = await navigator.hid.requestDevice({ filters: this.getFilters() });
|
|
124
|
+
const matched = devices.filter((device) => this.matchDevice(device));
|
|
125
|
+
if (devices.length > 0 && matched.length === 0) {
|
|
126
|
+
throw new Error('所选设备接口不正确,请选择 usagePage=0xff01、usage=0x10 的 AJAZZ 有线或 2.4G 接口');
|
|
127
|
+
}
|
|
128
|
+
return matched.map((device) => this.toDeviceInfo(device));
|
|
129
|
+
}
|
|
130
|
+
async init(id) {
|
|
131
|
+
const devices = await this.getDevices();
|
|
132
|
+
const target = devices.find((device) => device.id === id)?.raw;
|
|
133
|
+
if (!target) {
|
|
134
|
+
throw new Error(`未找到设备: ${id},请重新点击「添加新设备」授权`);
|
|
135
|
+
}
|
|
136
|
+
if (!this.matchesTargetCollection(target)) {
|
|
137
|
+
throw new Error('设备接口不匹配,请重新选择 usagePage=0xff01 / usage=0x10 的接口');
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
if (!target.opened) {
|
|
141
|
+
await target.open();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
throw this.formatSendError(error);
|
|
146
|
+
}
|
|
147
|
+
this.attachDevice(target);
|
|
148
|
+
return this.toDeviceInfo(target);
|
|
149
|
+
}
|
|
150
|
+
attachDevice(device) {
|
|
151
|
+
if (this.device && this.device !== device) {
|
|
152
|
+
this.detachDevice();
|
|
153
|
+
}
|
|
154
|
+
this.device = device;
|
|
155
|
+
device.addEventListener('inputreport', this.inputHandler);
|
|
156
|
+
}
|
|
157
|
+
detachDevice() {
|
|
158
|
+
if (!this.device) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
this.device.removeEventListener('inputreport', this.inputHandler);
|
|
162
|
+
this.device = null;
|
|
163
|
+
}
|
|
164
|
+
async closeDevice() {
|
|
165
|
+
this.rejectAll(new Error('设备已关闭'));
|
|
166
|
+
if (this.device?.opened) {
|
|
167
|
+
await this.device.close();
|
|
168
|
+
}
|
|
169
|
+
this.detachDevice();
|
|
170
|
+
}
|
|
171
|
+
get opened() {
|
|
172
|
+
return Boolean(this.device?.opened);
|
|
173
|
+
}
|
|
174
|
+
get currentDevice() {
|
|
175
|
+
return this.device ? this.toDeviceInfo(this.device) : null;
|
|
176
|
+
}
|
|
177
|
+
onInputReport(event) {
|
|
178
|
+
const raw = new Uint8Array(event.data.buffer, event.data.byteOffset, event.data.byteLength);
|
|
179
|
+
if (!isValidResponse(event.reportId, raw)) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const data = normalizeInputReport(event.reportId, raw);
|
|
183
|
+
this.notifyReportWaiters(data);
|
|
184
|
+
if (this.pending?.sent) {
|
|
185
|
+
const responseCommand = getResponseCommand(data);
|
|
186
|
+
const matchesPending = this.pending.acceptWriteResponse
|
|
187
|
+
? WRITE_RESPONSE_COMMANDS.has(responseCommand)
|
|
188
|
+
: responseCommand === this.pending.command;
|
|
189
|
+
if (matchesPending) {
|
|
190
|
+
const current = this.pending;
|
|
191
|
+
this.pending = null;
|
|
192
|
+
clearTimeout(current.timer);
|
|
193
|
+
current.resolve(data);
|
|
194
|
+
void this.flushQueue();
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const status = parseStatusReport(data);
|
|
199
|
+
if (status) {
|
|
200
|
+
this.emit('statusChange', {
|
|
201
|
+
dpiIndex: status.dpiIndex,
|
|
202
|
+
pollRateIndex: status.pollRateIndex,
|
|
203
|
+
pollRate: PollRateFromIndex[status.pollRateIndex] ?? 1000,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async sendAndWait(packet, timeout = this.timeout) {
|
|
208
|
+
return this.enqueue(packet, timeout, false);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* 原厂 webhid_write_command:写入后读取下一帧响应(命令字可能与请求不同)
|
|
212
|
+
*/
|
|
213
|
+
async sendWriteAndWait(packet, timeout = this.timeout) {
|
|
214
|
+
return this.enqueue(packet, timeout, true);
|
|
215
|
+
}
|
|
216
|
+
enqueue(packet, timeout, acceptWriteResponse) {
|
|
217
|
+
if (!this.device?.opened) {
|
|
218
|
+
throw new Error('设备未连接,请先调用 init()');
|
|
219
|
+
}
|
|
220
|
+
const command = packet[1] ?? -1;
|
|
221
|
+
return new Promise((resolve, reject) => {
|
|
222
|
+
const timer = setTimeout(() => {
|
|
223
|
+
if (this.pending?.resolve === resolve) {
|
|
224
|
+
this.pending = null;
|
|
225
|
+
}
|
|
226
|
+
this.queue = this.queue.filter((entry) => entry.resolve !== resolve);
|
|
227
|
+
reject(new Error(`命令 0x${command.toString(16)} 超时`));
|
|
228
|
+
void this.flushQueue();
|
|
229
|
+
}, timeout);
|
|
230
|
+
this.queue.push({
|
|
231
|
+
command,
|
|
232
|
+
packet,
|
|
233
|
+
resolve,
|
|
234
|
+
reject,
|
|
235
|
+
timer,
|
|
236
|
+
sent: false,
|
|
237
|
+
acceptWriteResponse,
|
|
238
|
+
});
|
|
239
|
+
void this.flushQueue();
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
async sendNoResponse(packet) {
|
|
243
|
+
if (!this.device?.opened) {
|
|
244
|
+
throw new Error('设备未连接,请先调用 init()');
|
|
245
|
+
}
|
|
246
|
+
await this.waitForIdle();
|
|
247
|
+
await this.device.sendReport(REPORT_ID, toPayloadBuffer(packet));
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* 等待设备主动上报并解析(如 Ping 触发的固件版本包)。
|
|
251
|
+
* 对齐原厂 getVersionInfo:先发 Ping,再监听 cmd=0x04 异步回包。
|
|
252
|
+
*/
|
|
253
|
+
waitForMatchingReport(matcher, timeout = this.timeout) {
|
|
254
|
+
return new Promise((resolve, reject) => {
|
|
255
|
+
const timer = setTimeout(() => {
|
|
256
|
+
this.reportWaiters = this.reportWaiters.filter((waiter) => waiter.resolve !== resolve);
|
|
257
|
+
reject(new Error('等待设备上报超时'));
|
|
258
|
+
}, timeout);
|
|
259
|
+
this.reportWaiters.push({ match: matcher, resolve, reject, timer });
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
notifyReportWaiters(data) {
|
|
263
|
+
for (const waiter of [...this.reportWaiters]) {
|
|
264
|
+
const result = waiter.match(data);
|
|
265
|
+
if (!result) {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
clearTimeout(waiter.timer);
|
|
269
|
+
this.reportWaiters = this.reportWaiters.filter((item) => item !== waiter);
|
|
270
|
+
waiter.resolve(result);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
async waitForIdle() {
|
|
274
|
+
while (this.pending || this.queue.length > 0 || this.flushing) {
|
|
275
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
async flushQueue() {
|
|
279
|
+
if (this.flushing || this.pending || !this.device?.opened || this.queue.length === 0) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
this.flushing = true;
|
|
283
|
+
const entry = this.queue.shift();
|
|
284
|
+
if (!entry) {
|
|
285
|
+
this.flushing = false;
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
this.pending = entry;
|
|
290
|
+
await this.device.sendReport(REPORT_ID, toPayloadBuffer(entry.packet));
|
|
291
|
+
entry.sent = true;
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
this.pending = null;
|
|
295
|
+
clearTimeout(entry.timer);
|
|
296
|
+
entry.reject(this.formatSendError(error));
|
|
297
|
+
void this.flushQueue();
|
|
298
|
+
}
|
|
299
|
+
finally {
|
|
300
|
+
this.flushing = false;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
rejectAll(error) {
|
|
304
|
+
for (const waiter of this.reportWaiters) {
|
|
305
|
+
clearTimeout(waiter.timer);
|
|
306
|
+
waiter.reject(error);
|
|
307
|
+
}
|
|
308
|
+
this.reportWaiters = [];
|
|
309
|
+
if (this.pending) {
|
|
310
|
+
clearTimeout(this.pending.timer);
|
|
311
|
+
this.pending.reject(error);
|
|
312
|
+
this.pending = null;
|
|
313
|
+
}
|
|
314
|
+
for (const entry of this.queue) {
|
|
315
|
+
clearTimeout(entry.timer);
|
|
316
|
+
entry.reject(error);
|
|
317
|
+
}
|
|
318
|
+
this.queue = [];
|
|
319
|
+
this.flushing = false;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
export function createDefaultConfigs() {
|
|
323
|
+
return [
|
|
324
|
+
{
|
|
325
|
+
vendorId: RECEIVER_VENDOR_ID,
|
|
326
|
+
productId: DEFAULT_PRODUCT_ID,
|
|
327
|
+
usagePage: DEFAULT_USAGE_PAGE,
|
|
328
|
+
usage: DEFAULT_USAGE,
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
vendorId: WIRED_VENDOR_ID,
|
|
332
|
+
productId: DEFAULT_PRODUCT_ID,
|
|
333
|
+
usagePage: DEFAULT_USAGE_PAGE,
|
|
334
|
+
usage: DEFAULT_USAGE,
|
|
335
|
+
},
|
|
336
|
+
];
|
|
337
|
+
}
|
|
338
|
+
export function createDefaultConfig() {
|
|
339
|
+
return createDefaultConfigs()[0];
|
|
340
|
+
}
|
|
341
|
+
//# sourceMappingURL=index.js.map
|