diff --git a/src/ajazz/index.ts b/src/ajazz/index.ts index edbb64e3..17c9abc6 100644 --- a/src/ajazz/index.ts +++ b/src/ajazz/index.ts @@ -412,6 +412,83 @@ export const AJAZZ_BUTTON_ACTIONS: ReadonlyArray<{ { label: "F10", type: 16, code1: 0, code2: 67, code3: 0 }, { label: "F11", type: 16, code1: 0, code2: 68, code3: 0 }, { label: "F12", type: 16, code1: 0, code2: 69, code3: 0 }, + // Letters, digits, punctuation, numpad and modifiers from the vendor's + // keyboard catalog (`newKeyInfos.json`), so a button can type a normal key + // (WASD etc.) rather than only media/navigation actions. + { label: "A", type: 16, code1: 0, code2: 4, code3: 0 }, + { label: "B", type: 16, code1: 0, code2: 5, code3: 0 }, + { label: "C", type: 16, code1: 0, code2: 6, code3: 0 }, + { label: "D", type: 16, code1: 0, code2: 7, code3: 0 }, + { label: "E", type: 16, code1: 0, code2: 8, code3: 0 }, + { label: "F", type: 16, code1: 0, code2: 9, code3: 0 }, + { label: "G", type: 16, code1: 0, code2: 10, code3: 0 }, + { label: "H", type: 16, code1: 0, code2: 11, code3: 0 }, + { label: "I", type: 16, code1: 0, code2: 12, code3: 0 }, + { label: "J", type: 16, code1: 0, code2: 13, code3: 0 }, + { label: "K", type: 16, code1: 0, code2: 14, code3: 0 }, + { label: "L", type: 16, code1: 0, code2: 15, code3: 0 }, + { label: "M", type: 16, code1: 0, code2: 16, code3: 0 }, + { label: "N", type: 16, code1: 0, code2: 17, code3: 0 }, + { label: "O", type: 16, code1: 0, code2: 18, code3: 0 }, + { label: "P", type: 16, code1: 0, code2: 19, code3: 0 }, + { label: "Q", type: 16, code1: 0, code2: 20, code3: 0 }, + { label: "R", type: 16, code1: 0, code2: 21, code3: 0 }, + { label: "S", type: 16, code1: 0, code2: 22, code3: 0 }, + { label: "T", type: 16, code1: 0, code2: 23, code3: 0 }, + { label: "U", type: 16, code1: 0, code2: 24, code3: 0 }, + { label: "V", type: 16, code1: 0, code2: 25, code3: 0 }, + { label: "W", type: 16, code1: 0, code2: 26, code3: 0 }, + { label: "X", type: 16, code1: 0, code2: 27, code3: 0 }, + { label: "Y", type: 16, code1: 0, code2: 28, code3: 0 }, + { label: "Z", type: 16, code1: 0, code2: 29, code3: 0 }, + { label: "1", type: 16, code1: 0, code2: 30, code3: 0 }, + { label: "2", type: 16, code1: 0, code2: 31, code3: 0 }, + { label: "3", type: 16, code1: 0, code2: 32, code3: 0 }, + { label: "4", type: 16, code1: 0, code2: 33, code3: 0 }, + { label: "5", type: 16, code1: 0, code2: 34, code3: 0 }, + { label: "6", type: 16, code1: 0, code2: 35, code3: 0 }, + { label: "7", type: 16, code1: 0, code2: 36, code3: 0 }, + { label: "8", type: 16, code1: 0, code2: 37, code3: 0 }, + { label: "9", type: 16, code1: 0, code2: 38, code3: 0 }, + { label: "0", type: 16, code1: 0, code2: 39, code3: 0 }, + { label: "Minus", type: 16, code1: 0, code2: 45, code3: 0 }, + { label: "Equals", type: 16, code1: 0, code2: 46, code3: 0 }, + { label: "Left Bracket", type: 16, code1: 0, code2: 47, code3: 0 }, + { label: "Right Bracket", type: 16, code1: 0, code2: 48, code3: 0 }, + { label: "Backslash", type: 16, code1: 0, code2: 49, code3: 0 }, + { label: "Semicolon", type: 16, code1: 0, code2: 51, code3: 0 }, + { label: "Quote", type: 16, code1: 0, code2: 52, code3: 0 }, + { label: "Backtick", type: 16, code1: 0, code2: 53, code3: 0 }, + { label: "Comma", type: 16, code1: 0, code2: 54, code3: 0 }, + { label: "Period", type: 16, code1: 0, code2: 55, code3: 0 }, + { label: "Slash", type: 16, code1: 0, code2: 56, code3: 0 }, + { label: "Num Lock", type: 16, code1: 0, code2: 83, code3: 0 }, + { label: "Num /", type: 16, code1: 0, code2: 84, code3: 0 }, + { label: "Num *", type: 16, code1: 0, code2: 85, code3: 0 }, + { label: "Num -", type: 16, code1: 0, code2: 86, code3: 0 }, + { label: "Num +", type: 16, code1: 0, code2: 87, code3: 0 }, + { label: "Num Enter", type: 16, code1: 0, code2: 88, code3: 0 }, + { label: "Num 1", type: 16, code1: 0, code2: 89, code3: 0 }, + { label: "Num 2", type: 16, code1: 0, code2: 90, code3: 0 }, + { label: "Num 3", type: 16, code1: 0, code2: 91, code3: 0 }, + { label: "Num 4", type: 16, code1: 0, code2: 92, code3: 0 }, + { label: "Num 5", type: 16, code1: 0, code2: 93, code3: 0 }, + { label: "Num 6", type: 16, code1: 0, code2: 94, code3: 0 }, + { label: "Num 7", type: 16, code1: 0, code2: 95, code3: 0 }, + { label: "Num 8", type: 16, code1: 0, code2: 96, code3: 0 }, + { label: "Num 9", type: 16, code1: 0, code2: 97, code3: 0 }, + { label: "Num 0", type: 16, code1: 0, code2: 98, code3: 0 }, + { label: "Num Del", type: 16, code1: 0, code2: 99, code3: 0 }, + { label: "Left Ctrl", type: 16, code1: 0, code2: 224, code3: 0 }, + { label: "Left Shift", type: 16, code1: 0, code2: 225, code3: 0 }, + { label: "Left Alt", type: 16, code1: 0, code2: 226, code3: 0 }, + { label: "Left Windows", type: 16, code1: 0, code2: 227, code3: 0 }, + { label: "Right Ctrl", type: 16, code1: 0, code2: 228, code3: 0 }, + { label: "Right Shift", type: 16, code1: 0, code2: 229, code3: 0 }, + { label: "Right Alt", type: 16, code1: 0, code2: 230, code3: 0 }, + { label: "Right Windows", type: 16, code1: 0, code2: 231, code3: 0 }, + { label: "Scroll Lock", type: 16, code1: 0, code2: 71, code3: 0 }, + { label: "Pause", type: 16, code1: 0, code2: 72, code3: 0 }, { label: "Left click", type: 32, code1: 1, code2: 0, code3: 0 }, { label: "Right click", type: 32, code1: 2, code2: 0, code3: 0 }, { label: "Middle click", type: 32, code1: 4, code2: 0, code3: 0 }, diff --git a/src/drivers/ajazz/hid.test.ts b/src/drivers/ajazz/hid.test.ts index b7f46cb9..6a9f5028 100644 --- a/src/drivers/ajazz/hid.test.ts +++ b/src/drivers/ajazz/hid.test.ts @@ -183,7 +183,13 @@ describe("ajazz codec", () => { assert.equal(ajazzBindingLabel({ type: 16, code1: 3, code2: 4, code3: 0 }), null); assert.deepEqual(ajazzFindButtonAction("Mute"), { type: 48, code1: 226, code2: 0, code3: 0 }); assert.equal(ajazzFindButtonAction("Nope"), null); - assert.ok(AJAZZ_BUTTON_ACTIONS.length > 40); + // Letters/digits map to their HID usage so a button can send "W" etc. + assert.deepEqual(ajazzFindButtonAction("W"), { type: 16, code1: 0, code2: 26, code3: 0 }); + assert.deepEqual(ajazzFindButtonAction("A"), { type: 16, code1: 0, code2: 4, code3: 0 }); + assert.deepEqual(ajazzFindButtonAction("0"), { type: 16, code1: 0, code2: 39, code3: 0 }); + assert.ok(AJAZZ_BUTTON_ACTIONS.length > 100); + // Labels must stay unique or the remap UI would pick the wrong binding. + assert.equal(new Set(AJAZZ_BUTTON_ACTIONS.map((a) => a.label)).size, AJAZZ_BUTTON_ACTIONS.length); assert.deepEqual([...AJAZZ_BUTTON_NAMES], ["Left", "Right", "Middle", "Backward", "Forward", "DPI"]); }); }); @@ -399,6 +405,16 @@ describe("AjazzHidClient", () => { assert.equal(status.buttonMappings?.["Forward"], "Mute"); }); + it("remaps a side button to a keyboard key like W", async () => { + const { client, keyBlock } = fakeMouse(); + await client.setButtonMapping("Forward", "W"); + const wire = new Uint8Array(63); + wire[0] = 0x08; + wire.set(keyBlock.slice(0, 24), 7); + assert.deepEqual(ajazzDecodeKeys(wire)![4], { type: 16, code1: 0, code2: 26, code3: 0 }); + assert.equal((await client.readStatus()).buttonMappings?.["Forward"], "W"); + }); + it("refuses unknown buttons, locked Left and unknown actions", async () => { const { client, sent } = fakeMouse(); await assert.rejects(client.setButtonMapping("Wheel", "Mute"), /has no "Wheel" button/);