@forgeax/engine-input 0.1.2

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.
Files changed (107) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +312 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/__tests__/browser-backend-dual-instance.test.d.ts +2 -0
  5. package/dist/__tests__/browser-backend-dual-instance.test.d.ts.map +1 -0
  6. package/dist/__tests__/browser-backend-focus-loss.test.d.ts +2 -0
  7. package/dist/__tests__/browser-backend-focus-loss.test.d.ts.map +1 -0
  8. package/dist/__tests__/browser-backend-lock-error.test.d.ts +2 -0
  9. package/dist/__tests__/browser-backend-lock-error.test.d.ts.map +1 -0
  10. package/dist/__tests__/browser-backend-lock-gate.test.d.ts +2 -0
  11. package/dist/__tests__/browser-backend-lock-gate.test.d.ts.map +1 -0
  12. package/dist/__tests__/browser-backend-lock-release.test.d.ts +2 -0
  13. package/dist/__tests__/browser-backend-lock-release.test.d.ts.map +1 -0
  14. package/dist/__tests__/browser-backend-lock-state.test.d.ts +2 -0
  15. package/dist/__tests__/browser-backend-lock-state.test.d.ts.map +1 -0
  16. package/dist/__tests__/canvas-input-boundary.test.d.ts +2 -0
  17. package/dist/__tests__/canvas-input-boundary.test.d.ts.map +1 -0
  18. package/dist/__tests__/canvas-input-owner.test-d.d.ts +2 -0
  19. package/dist/__tests__/canvas-input-owner.test-d.d.ts.map +1 -0
  20. package/dist/__tests__/composite-backend.test.d.ts +2 -0
  21. package/dist/__tests__/composite-backend.test.d.ts.map +1 -0
  22. package/dist/__tests__/controller-platform-owner.test-d.d.ts +2 -0
  23. package/dist/__tests__/controller-platform-owner.test-d.d.ts.map +1 -0
  24. package/dist/__tests__/input-snapshot.test-d.d.ts +2 -0
  25. package/dist/__tests__/input-snapshot.test-d.d.ts.map +1 -0
  26. package/dist/__tests__/input-snapshot.test.d.ts +2 -0
  27. package/dist/__tests__/input-snapshot.test.d.ts.map +1 -0
  28. package/dist/__tests__/swipe-direction-owner.test-d.d.ts +2 -0
  29. package/dist/__tests__/swipe-direction-owner.test-d.d.ts.map +1 -0
  30. package/dist/__tests__/ui-input-matrix.test.d.ts +2 -0
  31. package/dist/__tests__/ui-input-matrix.test.d.ts.map +1 -0
  32. package/dist/__tests__/ui-ownership-edge.test.d.ts +2 -0
  33. package/dist/__tests__/ui-ownership-edge.test.d.ts.map +1 -0
  34. package/dist/__tests__/ui-ownership.test.d.ts +2 -0
  35. package/dist/__tests__/ui-ownership.test.d.ts.map +1 -0
  36. package/dist/__tests__/ui-reset-idempotence.test.d.ts +2 -0
  37. package/dist/__tests__/ui-reset-idempotence.test.d.ts.map +1 -0
  38. package/dist/__tests__/ui-reset.test.d.ts +2 -0
  39. package/dist/__tests__/ui-reset.test.d.ts.map +1 -0
  40. package/dist/action-state.d.ts +146 -0
  41. package/dist/action-state.d.ts.map +1 -0
  42. package/dist/browser-backend.d.ts +121 -0
  43. package/dist/browser-backend.d.ts.map +1 -0
  44. package/dist/canvas-input-boundary.d.ts +13 -0
  45. package/dist/canvas-input-boundary.d.ts.map +1 -0
  46. package/dist/composite-backend.d.ts +34 -0
  47. package/dist/composite-backend.d.ts.map +1 -0
  48. package/dist/controller-db-data.d.ts +5 -0
  49. package/dist/controller-db-data.d.ts.map +1 -0
  50. package/dist/controller-db-data.mjs +12 -0
  51. package/dist/controller-db-data.mjs.map +1 -0
  52. package/dist/controller-db.d.ts +70 -0
  53. package/dist/controller-db.d.ts.map +1 -0
  54. package/dist/controller-db.mjs +119 -0
  55. package/dist/controller-db.mjs.map +1 -0
  56. package/dist/frame-start-scan-system.d.ts +37 -0
  57. package/dist/frame-start-scan-system.d.ts.map +1 -0
  58. package/dist/gamepad-frame.d.ts +42 -0
  59. package/dist/gamepad-frame.d.ts.map +1 -0
  60. package/dist/gesture-recognizer.d.ts +145 -0
  61. package/dist/gesture-recognizer.d.ts.map +1 -0
  62. package/dist/index.d.ts +13 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.mjs +1502 -0
  65. package/dist/index.mjs.map +1 -0
  66. package/dist/input-snapshot.d.ts +420 -0
  67. package/dist/input-snapshot.d.ts.map +1 -0
  68. package/dist/plugin-service.d.ts +11 -0
  69. package/dist/plugin-service.d.ts.map +1 -0
  70. package/dist/ui-ownership.d.ts +17 -0
  71. package/dist/ui-ownership.d.ts.map +1 -0
  72. package/dist/virtual-joystick.d.ts +39 -0
  73. package/dist/virtual-joystick.d.ts.map +1 -0
  74. package/package.json +72 -0
  75. package/src/__tests__/browser-backend-dual-instance.test.ts +379 -0
  76. package/src/__tests__/browser-backend-focus-loss.test.ts +239 -0
  77. package/src/__tests__/browser-backend-lock-error.test.ts +425 -0
  78. package/src/__tests__/browser-backend-lock-gate.test.ts +283 -0
  79. package/src/__tests__/browser-backend-lock-release.test.ts +641 -0
  80. package/src/__tests__/browser-backend-lock-state.test.ts +604 -0
  81. package/src/__tests__/canvas-input-boundary.test.ts +65 -0
  82. package/src/__tests__/canvas-input-owner.test-d.ts +39 -0
  83. package/src/__tests__/composite-backend.test.ts +299 -0
  84. package/src/__tests__/controller-platform-owner.test-d.ts +46 -0
  85. package/src/__tests__/input-snapshot.test-d.ts +78 -0
  86. package/src/__tests__/input-snapshot.test.ts +96 -0
  87. package/src/__tests__/swipe-direction-owner.test-d.ts +45 -0
  88. package/src/__tests__/ui-input-matrix.test.ts +31 -0
  89. package/src/__tests__/ui-ownership-edge.test.ts +33 -0
  90. package/src/__tests__/ui-ownership.test.ts +35 -0
  91. package/src/__tests__/ui-reset-idempotence.test.ts +17 -0
  92. package/src/__tests__/ui-reset.test.ts +25 -0
  93. package/src/action-state.ts +380 -0
  94. package/src/browser-backend.ts +884 -0
  95. package/src/canvas-input-boundary.ts +81 -0
  96. package/src/composite-backend.ts +189 -0
  97. package/src/controller-db-data.ts +25 -0
  98. package/src/controller-db.ts +218 -0
  99. package/src/frame-start-scan-system.ts +93 -0
  100. package/src/gamepad-frame.ts +259 -0
  101. package/src/gesture-recognizer.ts +495 -0
  102. package/src/index.ts +101 -0
  103. package/src/input-snapshot.ts +732 -0
  104. package/src/plugin-service.ts +31 -0
  105. package/src/ui-ownership.ts +61 -0
  106. package/src/virtual-joystick.ts +103 -0
  107. package/vendor/gamecontrollerdb.txt +2114 -0
@@ -0,0 +1,121 @@
1
+ import type { InputBackend, PointerType, VirtualJoystickConfig } from './input-snapshot';
2
+ export declare function coercePointerType(raw: string): PointerType;
3
+ /**
4
+ * Options accepted by `attachBrowserInputBackend`.
5
+ */
6
+ export interface BrowserInputBackendOptions {
7
+ /** Optional host-owned UI root. Events in this root never reach gameplay. */
8
+ readonly uiRoot?: Node;
9
+ /**
10
+ * Optional document handle (testing override). Defaults to `document`.
11
+ * Plan-strategy section 9 row 7 (`document.hasFocus()` semantics)
12
+ * relies on this to decide whether the up-edge set should be cleared
13
+ * when focus is lost.
14
+ */
15
+ readonly document?: Document;
16
+ /**
17
+ * Optional window handle (testing override). Defaults to `window`.
18
+ * Used to attach key listeners (capturing focus loss) without
19
+ * requiring the canvas itself to receive keyboard events.
20
+ */
21
+ readonly window?: Window;
22
+ /**
23
+ * Neutral gate for the auto PointerLock-on-click. When provided and it
24
+ * returns false, the click handler skips `requestPointerLock()` — the host
25
+ * decides whether a click should capture the cursor right now. Defaults to
26
+ * always-locking (returns true), so the standalone game runtime keeps its
27
+ * MVP behaviour unchanged. This predicate is deliberately host-opaque: the
28
+ * backend never learns WHY locking is (dis)allowed — it only asks. A host
29
+ * that mounts this canvas in a non-game context (e.g. an editor viewport
30
+ * that owns the cursor for orbit/pick) supplies a predicate that returns
31
+ * false there. The input package carries zero knowledge of those contexts.
32
+ */
33
+ readonly pointerLockAllowed?: () => boolean;
34
+ /**
35
+ * Optional navigator handle (testing override). Defaults to
36
+ * `globalThis.navigator`. Used to poll `navigator.getGamepads()` each
37
+ * frame. Mirrors the `document`/`window` override pattern.
38
+ */
39
+ readonly navigator?: {
40
+ getGamepads?(): (Gamepad | null)[];
41
+ };
42
+ /**
43
+ * Optional virtual joystick configurations (M3). When provided, the
44
+ * backend auto-binds the first pointerdown within each config's region
45
+ * and derives per-frame VirtualAxisSample outputs via deriveVirtualAxes.
46
+ */
47
+ readonly virtualJoysticks?: readonly VirtualJoystickConfig[];
48
+ /**
49
+ * Optional SDL controller DB text loader (M3 D-13 test injection). When
50
+ * omitted, the backend dynamic-imports the vendored 554KB DB from
51
+ * `@forgeax/engine-input/controller-db-data` on first sight of a
52
+ * non-standard gamepad (D-2 lazy-load: the 554KB never sits in the
53
+ * default path). Tests inject a synthetic DB string to avoid loading the
54
+ * real vendored file. Returns the raw gamecontrollerdb.txt contents.
55
+ */
56
+ readonly loadControllerDb?: () => Promise<string>;
57
+ /**
58
+ * Optional monotonic clock (M5 D-3 test injection). Defaults to
59
+ * `() => performance.now()`. The gesture recognizer advances all timers
60
+ * (long-press duration, double-tap / swipe windows) off this clock, NOT
61
+ * off pointer-event arrival, so recognition stays decoupled from event
62
+ * frequency (AC-16). Tests inject a fake clock for deterministic timing,
63
+ * mirroring the `document` / `window` / `navigator` override pattern.
64
+ */
65
+ readonly now?: () => number;
66
+ /**
67
+ * Optional lock provider (D-2). When provided, pointer-lock requests
68
+ * route through requestLock() / exitLock() instead of the W3C
69
+ * requestPointerLock / exitPointerLock API. The backend remains
70
+ * host-opaque -- it never learns whether the provider wraps Tauri
71
+ * native-grab, postMessage, or any other mechanism.
72
+ */
73
+ readonly lockProvider?: PointerLockProvider;
74
+ /**
75
+ * Optional callback for lock request failures (D-4). When W3C
76
+ * requestPointerLock rejects or lockProvider.requestLock/exitLock
77
+ * throws/rejects, the backend calls this with a detail object.
78
+ * Without this callback, failures are silently caught (backward
79
+ * compatible but contrary to charter P3).
80
+ */
81
+ readonly onLockError?: (detail: {
82
+ path: 'w3c' | 'provider';
83
+ cause: unknown;
84
+ }) => void;
85
+ }
86
+ /**
87
+ * Lock provider interface (D-2). A host injects this to replace the
88
+ * W3C Pointer Lock API path. requestLock() may return void (fire-and-forget,
89
+ * D-7 optimistic placement) or Promise<void> (awaitable). exitLock() is
90
+ * called when the backend needs to release the lock (ESC / blur / detach /
91
+ * setPointerLockAllowed(false)).
92
+ */
93
+ export interface PointerLockProvider {
94
+ /**
95
+ * Request pointer lock. The backend optimistically sets providerLocked=true
96
+ * when this is called (D-7). On throw/reject, the backend calls onLockError
97
+ * and rolls back providerLocked.
98
+ */
99
+ requestLock(): void | Promise<void>;
100
+ /** Release pointer lock. */
101
+ exitLock(): void;
102
+ }
103
+ /**
104
+ * Attach DOM listeners (keydown / keyup / mousedown / mouseup /
105
+ * pointerlockchange / mousemove inside lock / blur) to `canvas` and the
106
+ * surrounding window. Returns a `detach` callable that:
107
+ *
108
+ * - removes all listeners
109
+ * - exits PointerLock if currently locked
110
+ * - drops every internal accumulator
111
+ *
112
+ * The returned object also implements the `InputBackend` protocol -- the
113
+ * runtime inserts it under `INPUT_BACKEND_KEY` and adds the
114
+ * `InputFrameStartScan` system, which reads it back each tick.
115
+ * Calling `detach()` and the protocol's `detach()` are equivalent
116
+ * (idempotent; double-detach is safe -- charter P3).
117
+ */
118
+ export declare function attachBrowserInputBackend(canvas: HTMLCanvasElement, options?: BrowserInputBackendOptions): (() => void) & {
119
+ backend: InputBackend;
120
+ };
121
+ //# sourceMappingURL=browser-backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-backend.d.ts","sourceRoot":"","sources":["../src/browser-backend.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAGV,YAAY,EAIZ,WAAW,EAEX,qBAAqB,EACtB,MAAM,kBAAkB,CAAC;AAQ1B,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAI1D;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC;IACvB;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC;IAC5C;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE;QAAE,WAAW,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAA;KAAE,CAAC;IAC5D;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAC7D;;;;;;;OAOG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAClD;;;;;;;OAOG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,KAAK,GAAG,UAAU,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;CACvF;AAED;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,WAAW,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,4BAA4B;IAC5B,QAAQ,IAAI,IAAI,CAAC;CAClB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,iBAAiB,EACzB,OAAO,GAAE,0BAA+B,GACvC,CAAC,MAAM,IAAI,CAAC,GAAG;IAAE,OAAO,EAAE,YAAY,CAAA;CAAE,CAysB1C"}
@@ -0,0 +1,13 @@
1
+ import { type InputBackend } from './input-snapshot';
2
+ export interface CanvasInputBoundary {
3
+ readonly editor: InputBackend;
4
+ readonly game: InputBackend;
5
+ owner(): 'editor' | 'game';
6
+ grantGame(): void;
7
+ revokeGame(): void;
8
+ /** Clear the active consumer when an event originated in the host UI. */
9
+ handleUiEvent(event: Event, host: Node): boolean;
10
+ detach(): void;
11
+ }
12
+ export declare function createCanvasInputBoundary(source: InputBackend): CanvasInputBoundary;
13
+ //# sourceMappingURL=canvas-input-boundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-input-boundary.d.ts","sourceRoot":"","sources":["../src/canvas-input-boundary.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,KAAK,YAAY,EAElB,MAAM,kBAAkB,CAAC;AAG1B,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,KAAK,IAAI,QAAQ,GAAG,MAAM,CAAC;IAC3B,SAAS,IAAI,IAAI,CAAC;IAClB,UAAU,IAAI,IAAI,CAAC;IACnB,yEAAyE;IACzE,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC;IACjD,MAAM,IAAI,IAAI,CAAC;CAChB;AAID,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,YAAY,GAAG,mBAAmB,CAqDnF"}
@@ -0,0 +1,34 @@
1
+ import type { InputBackend } from './input-snapshot';
2
+ /** Options controlling the composite merge policy. */
3
+ export interface CompositeBackendOptions {
4
+ /**
5
+ * When `true` (default), a key held on the inner (human) backend suppresses
6
+ * injected state only for that same key in that frame. Set `false` for
7
+ * record/replay or AI-solo scenarios where both sources should coexist even
8
+ * on key overlap.
9
+ */
10
+ readonly yieldToHuman?: boolean;
11
+ }
12
+ /**
13
+ * A composite backend: an `InputBackend` (drop-in for `INPUT_BACKEND_KEY`) plus
14
+ * a programmatic injection surface. All injection is additive over the wrapped
15
+ * human backend.
16
+ */
17
+ export interface CompositeInputBackend extends InputBackend {
18
+ /** Hold `key` down (mirrors a keydown). Idempotent. */
19
+ press(key: string): void;
20
+ /** Release `key` (mirrors a keyup); emits a one-frame up-edge on next sample. */
21
+ release(key: string): void;
22
+ /** Set an injected mouse-button slot (0/1/2) held-state. */
23
+ setButton(slot: 0 | 1 | 2, down: boolean): void;
24
+ /** Accumulate injected pointer-lock movement delta (drained on next sample). */
25
+ addMovement(dx: number, dy: number): void;
26
+ /** Accumulate injected wheel notches (drained on next sample). */
27
+ addWheel(notches: number): void;
28
+ /** Drop all injected state; currently-held keys emit a clean up-edge once. */
29
+ clearInjected(): void;
30
+ /** Toggle the yield-to-human gate at runtime. */
31
+ setYieldToHuman(yield_: boolean): void;
32
+ }
33
+ export declare function makeCompositeBackend(inner: InputBackend, options?: CompositeBackendOptions): CompositeInputBackend;
34
+ //# sourceMappingURL=composite-backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composite-backend.d.ts","sourceRoot":"","sources":["../src/composite-backend.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,YAAY,EAAsB,MAAM,kBAAkB,CAAC;AAEzE,sDAAsD;AACtD,MAAM,WAAW,uBAAuB;IACtC;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAsB,SAAQ,YAAY;IACzD,uDAAuD;IACvD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,iFAAiF;IACjF,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,4DAA4D;IAC5D,SAAS,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAChD,gFAAgF;IAChF,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,8EAA8E;IAC9E,aAAa,IAAI,IAAI,CAAC;IACtB,iDAAiD;IACjD,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;CACxC;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,uBAAuB,GAChC,qBAAqB,CAsHvB"}
@@ -0,0 +1,5 @@
1
+ /** The vendored gamecontrollerdb.txt contents, inlined at build time. */
2
+ export declare const BUNDLED_CONTROLLER_DB: string;
3
+ /** Default loader used by the backend when no override is supplied (D-13). */
4
+ export declare function loadBundledControllerDb(): Promise<string>;
5
+ //# sourceMappingURL=controller-db-data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-db-data.d.ts","sourceRoot":"","sources":["../src/controller-db-data.ts"],"names":[],"mappings":"AAkBA,yEAAyE;AACzE,eAAO,MAAM,qBAAqB,EAAE,MAAsC,CAAC;AAE3E,8EAA8E;AAC9E,wBAAgB,uBAAuB,IAAI,OAAO,CAAC,MAAM,CAAC,CAEzD"}