diff --git a/app/src/js/gamepad.ts b/app/src/js/gamepad.ts new file mode 100644 index 0000000..ac589e4 --- /dev/null +++ b/app/src/js/gamepad.ts @@ -0,0 +1,64 @@ +//// +// Support for gamepads. +// Maps incoming gamepad button presses to keyboard buttons. +// + +const BUTTONS = [ + "A", "B", "X", "Y", + "LB", "RB", "LT", "RT", + "Select", "Start", + "LStick", "RStick", + "DPad Up", "DPad Down", "DPad Left", "DPad Right" +] + +const BUTTONS_TO_KEYS: Record = { + 0: "z", // A → Z + 1: "x", // B → X + 2: "c", // X → C + 3: "v", // Y → V + 12: "ArrowUp", + 13: "ArrowDown", + 14: "ArrowLeft", + 15: "ArrowRight", + 9: "Enter", // Start + 8: "Escape" // Select +} + +const activePads = new Set() +const prevState: Record = {} + +export function initGamepad() { + window.addEventListener("gamepadconnected", e => { + console.log("Gamepad connected:", e.gamepad) + activePads.add(e.gamepad.index) + requestAnimationFrame(handleGamepad) + }) + + window.addEventListener("gamepaddisconnected", e => { + console.log("Gamepad disconnected:", e.gamepad) + activePads.delete(e.gamepad.index) + }) +} + +function handleGamepad() { + const pads = navigator.getGamepads() + for (const gp of pads) { + if (!gp) continue + if (!prevState[gp.index]) prevState[gp.index] = gp.buttons.map(() => false) + + gp.buttons.forEach((btn, i) => { + const was = prevState[gp.index]![i] + const is = btn.pressed + if (was !== is) { + const key = BUTTONS_TO_KEYS[i] + if (key) { + const type = is ? "keydown" : "keyup" + window.dispatchEvent(new KeyboardEvent(type, { code: key, key })) + } + prevState[gp.index]![i] = is + } + }) + } + + requestAnimationFrame(handleGamepad) +} \ No newline at end of file diff --git a/app/src/js/main.ts b/app/src/js/main.ts index a2901af..df2c49c 100644 --- a/app/src/js/main.ts +++ b/app/src/js/main.ts @@ -2,6 +2,7 @@ import { initCompletion } from "./completion.js" import { initCursor } from "./cursor.js" import { initEditor } from "./editor.js" import { initFocus } from "./focus.js" +import { initGamepad } from "./gamepad.js" import { initHistory } from "./history.js" import { initHyperlink } from "./hyperlink.js" import { initInput } from "./input.js" @@ -13,6 +14,7 @@ initCompletion() initCursor() initFocus() initEditor() +initGamepad() initHistory() initHyperlink() initInput()