@flare-engine/input 0.2.1 → 0.3.1

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/dist/index.mjs CHANGED
@@ -1,541 +1,737 @@
1
- // src/pointer.ts
2
- function createPointer() {
3
- return {
4
- position: { x: 0, y: 0 },
5
- delta: { x: 0, y: 0 },
6
- isDown: false,
7
- justPressed: false,
8
- justReleased: false
9
- };
10
- }
11
- function resetPointerFrame(pointer) {
12
- pointer.delta.x = 0;
13
- pointer.delta.y = 0;
14
- pointer.justPressed = false;
15
- pointer.justReleased = false;
16
- }
17
- function pointerDown(pointer, x, y) {
18
- const wasDown = pointer.isDown;
19
- pointer.isDown = true;
20
- if (!wasDown) {
21
- pointer.justPressed = true;
22
- }
23
- pointer.delta.x = x - pointer.position.x;
24
- pointer.delta.y = y - pointer.position.y;
25
- pointer.position.x = x;
26
- pointer.position.y = y;
27
- }
28
- function pointerMove(pointer, x, y) {
29
- pointer.delta.x = x - pointer.position.x;
30
- pointer.delta.y = y - pointer.position.y;
31
- pointer.position.x = x;
32
- pointer.position.y = y;
33
- }
34
- function pointerUp(pointer, x, y) {
35
- pointer.isDown = false;
36
- pointer.justReleased = true;
37
- pointer.delta.x = x - pointer.position.x;
38
- pointer.delta.y = y - pointer.position.y;
39
- pointer.position.x = x;
40
- pointer.position.y = y;
41
- }
42
-
43
- // src/gamepad.ts
44
- var GamepadButton = {
45
- A: 0,
46
- B: 1,
47
- X: 2,
48
- Y: 3,
49
- LB: 4,
50
- RB: 5,
51
- LT: 6,
52
- RT: 7,
53
- Back: 8,
54
- Start: 9,
55
- LS: 10,
56
- RS: 11,
57
- DpadUp: 12,
58
- DpadDown: 13,
59
- DpadLeft: 14,
60
- DpadRight: 15,
61
- Home: 16
1
+ import { Signal } from "@flare-engine/events";
2
+ //#region src/gamepad.ts
3
+ /**
4
+ * Gamepad — poll-based state for a single connected controller.
5
+ *
6
+ * Buttons and axes are indexed against the W3C "Standard Gamepad" mapping by
7
+ * default. Callers can translate raw indices via `remap.ts` before feeding
8
+ * snapshots into `updateGamepadFromSnapshot`.
9
+ */
10
+ /** W3C Standard Gamepad button indices. */
11
+ const GamepadButton = {
12
+ A: 0,
13
+ B: 1,
14
+ X: 2,
15
+ Y: 3,
16
+ LB: 4,
17
+ RB: 5,
18
+ LT: 6,
19
+ RT: 7,
20
+ Back: 8,
21
+ Start: 9,
22
+ LS: 10,
23
+ RS: 11,
24
+ DpadUp: 12,
25
+ DpadDown: 13,
26
+ DpadLeft: 14,
27
+ DpadRight: 15,
28
+ Home: 16
62
29
  };
63
- var GamepadAxis = {
64
- LeftX: 0,
65
- LeftY: 1,
66
- RightX: 2,
67
- RightY: 3
30
+ /** W3C Standard Gamepad axes. */
31
+ const GamepadAxis = {
32
+ LeftX: 0,
33
+ LeftY: 1,
34
+ RightX: 2,
35
+ RightY: 3
68
36
  };
69
37
  function createGamepadState(index = 0, buttonCount = 17, axisCount = 4) {
70
- const buttons = new Array(buttonCount);
71
- for (let i = 0; i < buttonCount; i++) {
72
- buttons[i] = { pressed: false, value: 0, pressedThisFrame: false, releasedThisFrame: false };
73
- }
74
- const axes = new Array(axisCount).fill(0);
75
- return { connected: false, id: "", index, buttons, axes, timestamp: 0 };
38
+ const buttons = new Array(buttonCount);
39
+ for (let i = 0; i < buttonCount; i++) buttons[i] = {
40
+ pressed: false,
41
+ value: 0,
42
+ pressedThisFrame: false,
43
+ releasedThisFrame: false
44
+ };
45
+ return {
46
+ connected: false,
47
+ id: "",
48
+ index,
49
+ buttons,
50
+ axes: new Array(axisCount).fill(0),
51
+ timestamp: 0
52
+ };
76
53
  }
77
54
  function applyDeadZone(value, deadZone) {
78
- const abs = Math.abs(value);
79
- if (abs <= deadZone) return 0;
80
- const sign = value < 0 ? -1 : 1;
81
- return sign * ((abs - deadZone) / (1 - deadZone));
55
+ const abs = Math.abs(value);
56
+ if (abs <= deadZone) return 0;
57
+ return (value < 0 ? -1 : 1) * ((abs - deadZone) / (1 - deadZone));
82
58
  }
59
+ /**
60
+ * Apply a fresh snapshot to an existing `GamepadState` in place. Derived
61
+ * `pressedThisFrame` / `releasedThisFrame` flags are recomputed from the
62
+ * previous `pressed` value.
63
+ *
64
+ * `deadZone` in [0, 1). Values at or below are treated as 0.
65
+ */
83
66
  function updateGamepadFromSnapshot(state, snapshot, deadZone = 0) {
84
- if (snapshot === null) {
85
- if (!state.connected && !anyButtonDown(state)) {
86
- clearFrameFlags(state);
87
- return;
88
- }
89
- for (let i = 0; i < state.buttons.length; i++) {
90
- const b = state.buttons[i];
91
- if (!b) continue;
92
- b.pressedThisFrame = false;
93
- b.releasedThisFrame = b.pressed;
94
- b.pressed = false;
95
- b.value = 0;
96
- }
97
- for (let i = 0; i < state.axes.length; i++) state.axes[i] = 0;
98
- state.connected = false;
99
- state.id = "";
100
- return;
101
- }
102
- state.connected = true;
103
- state.id = snapshot.id;
104
- state.index = snapshot.index;
105
- state.timestamp = snapshot.timestamp;
106
- const srcButtons = snapshot.buttons;
107
- const bLen = Math.min(state.buttons.length, srcButtons.length);
108
- for (let i = 0; i < bLen; i++) {
109
- const prev = state.buttons[i];
110
- const src = srcButtons[i];
111
- if (!prev || !src) continue;
112
- const wasPressed = prev.pressed;
113
- prev.pressed = src.pressed;
114
- prev.value = src.value;
115
- prev.pressedThisFrame = src.pressed && !wasPressed;
116
- prev.releasedThisFrame = !src.pressed && wasPressed;
117
- }
118
- for (let i = bLen; i < state.buttons.length; i++) {
119
- const b = state.buttons[i];
120
- if (!b) continue;
121
- b.pressedThisFrame = false;
122
- b.releasedThisFrame = b.pressed;
123
- b.pressed = false;
124
- b.value = 0;
125
- }
126
- const aLen = Math.min(state.axes.length, snapshot.axes.length);
127
- for (let i = 0; i < aLen; i++) {
128
- state.axes[i] = applyDeadZone(snapshot.axes[i] ?? 0, deadZone);
129
- }
130
- for (let i = aLen; i < state.axes.length; i++) state.axes[i] = 0;
67
+ if (snapshot === null) {
68
+ if (!state.connected && !anyButtonDown(state)) {
69
+ clearFrameFlags(state);
70
+ return;
71
+ }
72
+ for (let i = 0; i < state.buttons.length; i++) {
73
+ const b = state.buttons[i];
74
+ if (!b) continue;
75
+ b.pressedThisFrame = false;
76
+ b.releasedThisFrame = b.pressed;
77
+ b.pressed = false;
78
+ b.value = 0;
79
+ }
80
+ for (let i = 0; i < state.axes.length; i++) state.axes[i] = 0;
81
+ state.connected = false;
82
+ state.id = "";
83
+ return;
84
+ }
85
+ state.connected = true;
86
+ state.id = snapshot.id;
87
+ state.index = snapshot.index;
88
+ state.timestamp = snapshot.timestamp;
89
+ const srcButtons = snapshot.buttons;
90
+ const bLen = Math.min(state.buttons.length, srcButtons.length);
91
+ for (let i = 0; i < bLen; i++) {
92
+ const prev = state.buttons[i];
93
+ const src = srcButtons[i];
94
+ if (!prev || !src) continue;
95
+ const wasPressed = prev.pressed;
96
+ prev.pressed = src.pressed;
97
+ prev.value = src.value;
98
+ prev.pressedThisFrame = src.pressed && !wasPressed;
99
+ prev.releasedThisFrame = !src.pressed && wasPressed;
100
+ }
101
+ for (let i = bLen; i < state.buttons.length; i++) {
102
+ const b = state.buttons[i];
103
+ if (!b) continue;
104
+ b.pressedThisFrame = false;
105
+ b.releasedThisFrame = b.pressed;
106
+ b.pressed = false;
107
+ b.value = 0;
108
+ }
109
+ const aLen = Math.min(state.axes.length, snapshot.axes.length);
110
+ for (let i = 0; i < aLen; i++) state.axes[i] = applyDeadZone(snapshot.axes[i] ?? 0, deadZone);
111
+ for (let i = aLen; i < state.axes.length; i++) state.axes[i] = 0;
131
112
  }
113
+ /** Reset just the per-frame edge flags. Call when advancing a frame without a fresh snapshot. */
132
114
  function resetGamepadFrame(state) {
133
- clearFrameFlags(state);
115
+ clearFrameFlags(state);
134
116
  }
135
117
  function clearFrameFlags(state) {
136
- for (let i = 0; i < state.buttons.length; i++) {
137
- const b = state.buttons[i];
138
- if (!b) continue;
139
- b.pressedThisFrame = false;
140
- b.releasedThisFrame = false;
141
- }
118
+ for (let i = 0; i < state.buttons.length; i++) {
119
+ const b = state.buttons[i];
120
+ if (!b) continue;
121
+ b.pressedThisFrame = false;
122
+ b.releasedThisFrame = false;
123
+ }
142
124
  }
143
125
  function anyButtonDown(state) {
144
- for (let i = 0; i < state.buttons.length; i++) {
145
- if (state.buttons[i]?.pressed) return true;
146
- }
147
- return false;
126
+ for (let i = 0; i < state.buttons.length; i++) if (state.buttons[i]?.pressed) return true;
127
+ return false;
148
128
  }
149
129
  function isButtonDown(state, button) {
150
- return state.buttons[button]?.pressed ?? false;
130
+ return state.buttons[button]?.pressed ?? false;
151
131
  }
152
132
  function wasButtonPressed(state, button) {
153
- return state.buttons[button]?.pressedThisFrame ?? false;
133
+ return state.buttons[button]?.pressedThisFrame ?? false;
154
134
  }
155
135
  function wasButtonReleased(state, button) {
156
- return state.buttons[button]?.releasedThisFrame ?? false;
136
+ return state.buttons[button]?.releasedThisFrame ?? false;
157
137
  }
158
138
  function getAxis(state, axis) {
159
- return state.axes[axis] ?? 0;
139
+ return state.axes[axis] ?? 0;
160
140
  }
161
-
162
- // src/remap.ts
141
+ //#endregion
142
+ //#region src/gamepad-source.ts
143
+ /**
144
+ * Wrap `navigator.getGamepads()` (W3C Gamepad API). If `nav` is not supplied,
145
+ * uses `globalThis.navigator`. Returns an empty array when the API is absent.
146
+ */
147
+ function createWebGamepadSource(nav) {
148
+ const source = nav ?? (typeof globalThis === "undefined" ? void 0 : globalThis.navigator);
149
+ return { poll() {
150
+ if (!source?.getGamepads) return [];
151
+ const pads = source.getGamepads();
152
+ const out = new Array(pads.length);
153
+ for (let i = 0; i < pads.length; i++) {
154
+ const p = pads[i];
155
+ if (!p?.connected) {
156
+ out[i] = null;
157
+ continue;
158
+ }
159
+ out[i] = webGamepadToSnapshot(p);
160
+ }
161
+ return out;
162
+ } };
163
+ }
164
+ function webGamepadToSnapshot(p) {
165
+ const buttons = new Array(p.buttons.length);
166
+ for (let i = 0; i < p.buttons.length; i++) {
167
+ const b = p.buttons[i];
168
+ buttons[i] = {
169
+ pressed: b?.pressed ?? false,
170
+ value: b?.value ?? 0
171
+ };
172
+ }
173
+ const axes = new Array(p.axes.length);
174
+ for (let i = 0; i < p.axes.length; i++) axes[i] = p.axes[i] ?? 0;
175
+ return {
176
+ id: p.id,
177
+ index: p.index,
178
+ timestamp: p.timestamp,
179
+ buttons,
180
+ axes
181
+ };
182
+ }
183
+ //#endregion
184
+ //#region src/gestures.ts
185
+ const DEFAULT_TAP_MAX_DURATION = .3;
186
+ const DEFAULT_TAP_MAX_DISTANCE = 20;
187
+ const DEFAULT_LONG_PRESS_MIN_DURATION = .5;
188
+ const DEFAULT_SWIPE_MIN_DISTANCE = 50;
189
+ const DEFAULT_SWIPE_MAX_DURATION = .4;
190
+ /**
191
+ * GestureDetector recognizes tap, long press, swipe, and pinch gestures
192
+ * from raw pointer events.
193
+ */
194
+ var GestureDetector = class {
195
+ onTap = new Signal();
196
+ onLongPress = new Signal();
197
+ onSwipe = new Signal();
198
+ onPinch = new Signal();
199
+ tapMaxDuration;
200
+ tapMaxDistance;
201
+ longPressMinDuration;
202
+ swipeMinDistance;
203
+ swipeMaxDuration;
204
+ _startPos = {
205
+ x: 0,
206
+ y: 0
207
+ };
208
+ _startTime = 0;
209
+ _isTracking = false;
210
+ _longPressFired = false;
211
+ _pinchStartDistance = 0;
212
+ _pinchTouches = null;
213
+ constructor(config) {
214
+ this.tapMaxDuration = config?.tapMaxDuration ?? DEFAULT_TAP_MAX_DURATION;
215
+ this.tapMaxDistance = config?.tapMaxDistance ?? DEFAULT_TAP_MAX_DISTANCE;
216
+ this.longPressMinDuration = config?.longPressMinDuration ?? DEFAULT_LONG_PRESS_MIN_DURATION;
217
+ this.swipeMinDistance = config?.swipeMinDistance ?? DEFAULT_SWIPE_MIN_DISTANCE;
218
+ this.swipeMaxDuration = config?.swipeMaxDuration ?? DEFAULT_SWIPE_MAX_DURATION;
219
+ }
220
+ /** Feed a pointer-down event. */
221
+ pointerDown(x, y) {
222
+ this._startPos = {
223
+ x,
224
+ y
225
+ };
226
+ this._startTime = 0;
227
+ this._isTracking = true;
228
+ this._longPressFired = false;
229
+ }
230
+ /** Feed a pointer-up event. */
231
+ pointerUp(x, y, elapsedSinceDown) {
232
+ if (!this._isTracking) return;
233
+ this._isTracking = false;
234
+ const dx = x - this._startPos.x;
235
+ const dy = y - this._startPos.y;
236
+ const dist = Math.sqrt(dx * dx + dy * dy);
237
+ const duration = elapsedSinceDown;
238
+ if (dist >= this.swipeMinDistance && duration <= this.swipeMaxDuration) {
239
+ const absDx = Math.abs(dx);
240
+ const absDy = Math.abs(dy);
241
+ let direction;
242
+ if (absDx > absDy) direction = dx > 0 ? "right" : "left";
243
+ else direction = dy > 0 ? "down" : "up";
244
+ this.onSwipe.emit({
245
+ direction,
246
+ distance: dist,
247
+ duration
248
+ });
249
+ return;
250
+ }
251
+ if (dist <= this.tapMaxDistance && duration <= this.tapMaxDuration) {
252
+ this.onTap.emit({
253
+ x,
254
+ y
255
+ });
256
+ return;
257
+ }
258
+ }
259
+ /**
260
+ * Update gesture tracking. Call each frame with dt.
261
+ * @param dt Delta time in seconds.
262
+ * @param currentX Current pointer X.
263
+ * @param currentY Current pointer Y.
264
+ */
265
+ update(dt, currentX, currentY) {
266
+ if (!this._isTracking) return;
267
+ this._startTime += dt;
268
+ if (!this._longPressFired && this._startTime >= this.longPressMinDuration) {
269
+ const dx = currentX - this._startPos.x;
270
+ const dy = currentY - this._startPos.y;
271
+ if (Math.sqrt(dx * dx + dy * dy) <= this.tapMaxDistance) {
272
+ this._longPressFired = true;
273
+ this.onLongPress.emit({
274
+ x: currentX,
275
+ y: currentY
276
+ });
277
+ }
278
+ }
279
+ }
280
+ /**
281
+ * Feed a two-finger pinch update. Call when two touches are active.
282
+ * @param t1 First touch position.
283
+ * @param t2 Second touch position.
284
+ */
285
+ pinchUpdate(t1, t2) {
286
+ const dx = t2.x - t1.x;
287
+ const dy = t2.y - t1.y;
288
+ const dist = Math.sqrt(dx * dx + dy * dy);
289
+ if (this._pinchTouches === null) {
290
+ this._pinchStartDistance = dist;
291
+ this._pinchTouches = [{ ...t1 }, { ...t2 }];
292
+ return;
293
+ }
294
+ if (this._pinchStartDistance > 0) {
295
+ const scale = dist / this._pinchStartDistance;
296
+ const center = {
297
+ x: (t1.x + t2.x) / 2,
298
+ y: (t1.y + t2.y) / 2
299
+ };
300
+ this.onPinch.emit({
301
+ scale,
302
+ center
303
+ });
304
+ }
305
+ }
306
+ /** Call when pinch ends (fewer than 2 touches). */
307
+ pinchEnd() {
308
+ this._pinchTouches = null;
309
+ this._pinchStartDistance = 0;
310
+ }
311
+ };
312
+ //#endregion
313
+ //#region src/virtual-button.ts
314
+ /** Create a default button state. */
315
+ function createButtonState() {
316
+ return {
317
+ isPressed: false,
318
+ justPressed: false,
319
+ justReleased: false
320
+ };
321
+ }
322
+ /** Call at the start of each frame to reset per-frame flags. */
323
+ function resetButtonFrame(state) {
324
+ state.justPressed = false;
325
+ state.justReleased = false;
326
+ }
327
+ /** Call when the button is pressed. */
328
+ function pressButton(state) {
329
+ if (!state.isPressed) {
330
+ state.isPressed = true;
331
+ state.justPressed = true;
332
+ }
333
+ }
334
+ /** Call when the button is released. */
335
+ function releaseButton(state) {
336
+ if (state.isPressed) {
337
+ state.isPressed = false;
338
+ state.justReleased = true;
339
+ }
340
+ }
341
+ //#endregion
342
+ //#region src/keyboard.ts
343
+ /** Create a fresh keyboard state. */
344
+ function createKeyboard() {
345
+ const states = [];
346
+ const codeIndex = /* @__PURE__ */ new Map();
347
+ return {
348
+ isDown(code) {
349
+ const i = codeIndex.get(code);
350
+ if (i === void 0) return false;
351
+ return states[i]?.isPressed ?? false;
352
+ },
353
+ wasPressed(code) {
354
+ const i = codeIndex.get(code);
355
+ if (i === void 0) return false;
356
+ return states[i]?.justPressed ?? false;
357
+ },
358
+ wasReleased(code) {
359
+ const i = codeIndex.get(code);
360
+ if (i === void 0) return false;
361
+ return states[i]?.justReleased ?? false;
362
+ },
363
+ keyDown(code) {
364
+ let i = codeIndex.get(code);
365
+ if (i === void 0) {
366
+ i = states.length;
367
+ states.push(createButtonState());
368
+ codeIndex.set(code, i);
369
+ }
370
+ pressButton(states[i]);
371
+ },
372
+ keyUp(code) {
373
+ const i = codeIndex.get(code);
374
+ if (i === void 0) return;
375
+ releaseButton(states[i]);
376
+ },
377
+ reset() {
378
+ for (let i = 0; i < states.length; i++) resetButtonFrame(states[i]);
379
+ },
380
+ clear() {
381
+ for (let i = 0; i < states.length; i++) releaseButton(states[i]);
382
+ }
383
+ };
384
+ }
385
+ /** Free-function parity with `resetPointerFrame`. Delegates to `kb.reset()`. */
386
+ function resetKeyboardFrame(kb) {
387
+ kb.reset();
388
+ }
389
+ /**
390
+ * Web turnkey adapter: registers `keydown`, `keyup`, and `blur` listeners on
391
+ * `target`. `blur` calls `kb.clear()` (stuck-key guard). Returns a detach fn.
392
+ *
393
+ * `target` is structural `{ addEventListener, removeEventListener }` — no DOM
394
+ * types dep. On web, pass `window`; for mobile/RN, feed `keyDown`/`keyUp`
395
+ * directly from a hardware-keyboard native module or an on-screen source.
396
+ *
397
+ * Does NOT call `preventDefault` — bare use lets Space/arrows scroll and Tab
398
+ * move DOM focus. For game-mode input, attach your own listener that calls
399
+ * `preventDefault` for game keys and forwards `kb.keyDown(ev.code)`.
400
+ */
401
+ function attachKeyboardListeners(kb, target) {
402
+ const onKeyDown = (ev) => {
403
+ kb.keyDown(ev.code);
404
+ };
405
+ const onKeyUp = (ev) => {
406
+ kb.keyUp(ev.code);
407
+ };
408
+ const onBlur = (_ev) => {
409
+ kb.clear();
410
+ };
411
+ target.addEventListener("keydown", onKeyDown);
412
+ target.addEventListener("keyup", onKeyUp);
413
+ target.addEventListener("blur", onBlur);
414
+ return () => {
415
+ target.removeEventListener("keydown", onKeyDown);
416
+ target.removeEventListener("keyup", onKeyUp);
417
+ target.removeEventListener("blur", onBlur);
418
+ };
419
+ }
420
+ /** True if any bound code for `action` is currently held. */
421
+ function isActionDown(kb, bindings, action) {
422
+ const codes = bindings[action];
423
+ if (!codes) return false;
424
+ for (let i = 0; i < codes.length; i++) if (kb.isDown(codes[i])) return true;
425
+ return false;
426
+ }
427
+ /** True if any bound code for `action` was pressed this frame. */
428
+ function wasActionPressed(kb, bindings, action) {
429
+ const codes = bindings[action];
430
+ if (!codes) return false;
431
+ for (let i = 0; i < codes.length; i++) if (kb.wasPressed(codes[i])) return true;
432
+ return false;
433
+ }
434
+ /**
435
+ * True if any bound code for `action` was released this frame AND no bound
436
+ * code is still held. Release fires only when the last held bound key lifts.
437
+ */
438
+ function wasActionReleased(kb, bindings, action) {
439
+ const codes = bindings[action];
440
+ if (!codes) return false;
441
+ let anyReleased = false;
442
+ for (let i = 0; i < codes.length; i++) {
443
+ if (kb.isDown(codes[i])) return false;
444
+ if (kb.wasReleased(codes[i])) anyReleased = true;
445
+ }
446
+ return anyReleased;
447
+ }
448
+ //#endregion
449
+ //#region src/pointer.ts
450
+ /** Create a default pointer state. */
451
+ function createPointer() {
452
+ return {
453
+ position: {
454
+ x: 0,
455
+ y: 0
456
+ },
457
+ delta: {
458
+ x: 0,
459
+ y: 0
460
+ },
461
+ isDown: false,
462
+ justPressed: false,
463
+ justReleased: false
464
+ };
465
+ }
466
+ /**
467
+ * Update pointer state for a new frame.
468
+ * Call this at the start of each frame to clear per-frame flags.
469
+ */
470
+ function resetPointerFrame(pointer) {
471
+ pointer.delta.x = 0;
472
+ pointer.delta.y = 0;
473
+ pointer.justPressed = false;
474
+ pointer.justReleased = false;
475
+ }
476
+ /** Feed a pointer-down event into the pointer state. */
477
+ function pointerDown(pointer, x, y) {
478
+ const wasDown = pointer.isDown;
479
+ pointer.isDown = true;
480
+ if (!wasDown) pointer.justPressed = true;
481
+ pointer.delta.x += x - pointer.position.x;
482
+ pointer.delta.y += y - pointer.position.y;
483
+ pointer.position.x = x;
484
+ pointer.position.y = y;
485
+ }
486
+ /** Feed a pointer-move event into the pointer state. */
487
+ function pointerMove(pointer, x, y) {
488
+ pointer.delta.x += x - pointer.position.x;
489
+ pointer.delta.y += y - pointer.position.y;
490
+ pointer.position.x = x;
491
+ pointer.position.y = y;
492
+ }
493
+ /** Feed a pointer-up event into the pointer state. */
494
+ function pointerUp(pointer, x, y) {
495
+ pointer.isDown = false;
496
+ pointer.justReleased = true;
497
+ pointer.delta.x += x - pointer.position.x;
498
+ pointer.delta.y += y - pointer.position.y;
499
+ pointer.position.x = x;
500
+ pointer.position.y = y;
501
+ }
502
+ //#endregion
503
+ //#region src/remap.ts
163
504
  function createIdentityRemap(buttonCount = 17, axisCount = 4) {
164
- const buttons = new Array(buttonCount);
165
- for (let i = 0; i < buttonCount; i++) buttons[i] = i;
166
- const axes = new Array(axisCount);
167
- for (let i = 0; i < axisCount; i++) axes[i] = { index: i, invert: false, scale: 1 };
168
- return { buttons, axes };
505
+ const buttons = new Array(buttonCount);
506
+ for (let i = 0; i < buttonCount; i++) buttons[i] = i;
507
+ const axes = new Array(axisCount);
508
+ for (let i = 0; i < axisCount; i++) axes[i] = {
509
+ index: i,
510
+ invert: false,
511
+ scale: 1
512
+ };
513
+ return {
514
+ buttons,
515
+ axes
516
+ };
169
517
  }
518
+ /** Apply a remap table to a raw snapshot, returning a new snapshot with logical ordering. */
170
519
  function applyRemap(snapshot, table) {
171
- const buttons = new Array(table.buttons.length);
172
- for (let logical = 0; logical < table.buttons.length; logical++) {
173
- const physical = table.buttons[logical] ?? logical;
174
- const src = snapshot.buttons[physical];
175
- buttons[logical] = { pressed: src?.pressed ?? false, value: src?.value ?? 0 };
176
- }
177
- const axes = new Array(table.axes.length);
178
- for (let logical = 0; logical < table.axes.length; logical++) {
179
- const map = table.axes[logical] ?? { index: logical, invert: false, scale: 1 };
180
- const raw = snapshot.axes[map.index] ?? 0;
181
- axes[logical] = raw * map.scale * (map.invert ? -1 : 1);
182
- }
183
- return { id: snapshot.id, index: snapshot.index, timestamp: snapshot.timestamp, buttons, axes };
520
+ const buttons = new Array(table.buttons.length);
521
+ for (let logical = 0; logical < table.buttons.length; logical++) {
522
+ const physical = table.buttons[logical] ?? logical;
523
+ const src = snapshot.buttons[physical];
524
+ buttons[logical] = {
525
+ pressed: src?.pressed ?? false,
526
+ value: src?.value ?? 0
527
+ };
528
+ }
529
+ const axes = new Array(table.axes.length);
530
+ for (let logical = 0; logical < table.axes.length; logical++) {
531
+ const map = table.axes[logical] ?? {
532
+ index: logical,
533
+ invert: false,
534
+ scale: 1
535
+ };
536
+ axes[logical] = (snapshot.axes[map.index] ?? 0) * map.scale * (map.invert ? -1 : 1);
537
+ }
538
+ return {
539
+ id: snapshot.id,
540
+ index: snapshot.index,
541
+ timestamp: snapshot.timestamp,
542
+ buttons,
543
+ axes
544
+ };
184
545
  }
546
+ /** Persist a remap table under the given key. Throws on invalid table shape. */
185
547
  async function saveRemap(store, key, table) {
186
- await store.set(key, JSON.stringify(table));
548
+ await store.set(key, JSON.stringify(table));
187
549
  }
550
+ /** Load a remap table. Returns `null` if absent or malformed. */
188
551
  async function loadRemap(store, key) {
189
- const raw = await store.get(key);
190
- if (raw === null) return null;
191
- try {
192
- const parsed = JSON.parse(raw);
193
- if (!isRemapTable(parsed)) return null;
194
- return parsed;
195
- } catch {
196
- return null;
197
- }
552
+ const raw = await store.get(key);
553
+ if (raw === null) return null;
554
+ try {
555
+ const parsed = JSON.parse(raw);
556
+ if (!isRemapTable(parsed)) return null;
557
+ return parsed;
558
+ } catch {
559
+ return null;
560
+ }
198
561
  }
199
562
  function isRemapTable(value) {
200
- if (typeof value !== "object" || value === null) return false;
201
- const obj = value;
202
- if (!Array.isArray(obj.buttons) || !Array.isArray(obj.axes)) return false;
203
- for (const b of obj.buttons) if (typeof b !== "number") return false;
204
- for (const a of obj.axes) {
205
- if (typeof a !== "object" || a === null) return false;
206
- const ax = a;
207
- if (typeof ax.index !== "number" || typeof ax.invert !== "boolean" || typeof ax.scale !== "number") {
208
- return false;
209
- }
210
- }
211
- return true;
563
+ if (typeof value !== "object" || value === null) return false;
564
+ const obj = value;
565
+ if (!Array.isArray(obj.buttons) || !Array.isArray(obj.axes)) return false;
566
+ for (const b of obj.buttons) if (typeof b !== "number") return false;
567
+ for (const a of obj.axes) {
568
+ if (typeof a !== "object" || a === null) return false;
569
+ const ax = a;
570
+ if (typeof ax.index !== "number" || typeof ax.invert !== "boolean" || typeof ax.scale !== "number") return false;
571
+ }
572
+ return true;
212
573
  }
213
-
214
- // src/input-manager.ts
574
+ //#endregion
575
+ //#region src/input-manager.ts
576
+ /**
577
+ * InputManager provides a polling-based input model.
578
+ *
579
+ * Call `update()` once per frame (before game logic) to snapshot input state.
580
+ * Feed events from the platform (RN touch/gesture handlers) via the on* methods.
581
+ */
215
582
  var InputManager = class {
216
- /** Primary pointer (first touch or mouse). */
217
- pointer = createPointer();
218
- /** All active touch points. */
219
- touches = [];
220
- _touchMap = /* @__PURE__ */ new Map();
221
- _gamepads = [];
222
- _gamepadDeadZone = 0.15;
223
- _gamepadRemaps = /* @__PURE__ */ new Map();
224
- /** Call once per frame to reset per-frame flags. */
225
- update() {
226
- resetPointerFrame(this.pointer);
227
- for (let i = 0; i < this._gamepads.length; i++) {
228
- const pad = this._gamepads[i];
229
- if (pad) resetGamepadFrame(pad);
230
- }
231
- }
232
- /** Feed a touch/pointer begin event. */
233
- onPointerDown(id, x, y) {
234
- pointerDown(this.pointer, x, y);
235
- this._addOrUpdateTouch(id, x, y, true);
236
- }
237
- /** Feed a touch/pointer move event. */
238
- onPointerMove(id, x, y) {
239
- pointerMove(this.pointer, x, y);
240
- this._addOrUpdateTouch(id, x, y, true);
241
- }
242
- /** Feed a touch/pointer end event. */
243
- onPointerUp(id, x, y) {
244
- pointerUp(this.pointer, x, y);
245
- this._removeTouch(id);
246
- }
247
- /** Access the gamepad state at `index`. Allocates on first access. */
248
- gamepad(index) {
249
- let pad = this._gamepads[index];
250
- if (!pad) {
251
- pad = createGamepadState(index);
252
- this._gamepads[index] = pad;
253
- }
254
- return pad;
255
- }
256
- /** Dead zone in [0, 1). Axes at or below this magnitude read as 0. */
257
- setGamepadDeadZone(deadZone) {
258
- this._gamepadDeadZone = Math.max(0, Math.min(0.95, deadZone));
259
- }
260
- /** Install a remap table for a specific gamepad index. Pass `null` to clear. */
261
- setGamepadRemap(index, table) {
262
- if (table === null) this._gamepadRemaps.delete(index);
263
- else this._gamepadRemaps.set(index, table);
264
- }
265
- /** Poll all gamepads from the given source, applying remap + dead zones. */
266
- pollGamepads(source) {
267
- const snapshots = source.poll();
268
- for (let i = 0; i < snapshots.length; i++) {
269
- const pad = this.gamepad(i);
270
- const snap = snapshots[i] ?? null;
271
- const remapped = snap !== null ? this._gamepadRemaps.get(i) ? applyRemap(snap, this._gamepadRemaps.get(i)) : snap : null;
272
- updateGamepadFromSnapshot(pad, remapped, this._gamepadDeadZone);
273
- }
274
- }
275
- _addOrUpdateTouch(id, x, y, isDown) {
276
- let touch = this._touchMap.get(id);
277
- if (!touch) {
278
- touch = { id, position: { x, y }, isDown };
279
- this._touchMap.set(id, touch);
280
- this.touches.push(touch);
281
- } else {
282
- touch.position.x = x;
283
- touch.position.y = y;
284
- touch.isDown = isDown;
285
- }
286
- }
287
- _removeTouch(id) {
288
- const touch = this._touchMap.get(id);
289
- if (!touch) return;
290
- this._touchMap.delete(id);
291
- const idx = this.touches.indexOf(touch);
292
- if (idx !== -1) {
293
- this.touches.splice(idx, 1);
294
- }
295
- }
583
+ /** Primary pointer (first touch or mouse). */
584
+ pointer = createPointer();
585
+ /** Buffered keyboard state. Feed via `attachKeyboardListeners` (web) or `keyboard.keyDown`/`keyUp` (mobile/RN). */
586
+ keyboard = createKeyboard();
587
+ /** All active touch points. */
588
+ touches = [];
589
+ _touchMap = /* @__PURE__ */ new Map();
590
+ _gamepads = [];
591
+ _gamepadDeadZone = .15;
592
+ _gamepadRemaps = /* @__PURE__ */ new Map();
593
+ /** Call once per frame to reset per-frame flags. */
594
+ update() {
595
+ resetPointerFrame(this.pointer);
596
+ resetKeyboardFrame(this.keyboard);
597
+ for (let i = 0; i < this._gamepads.length; i++) {
598
+ const pad = this._gamepads[i];
599
+ if (pad) resetGamepadFrame(pad);
600
+ }
601
+ }
602
+ /** Feed a touch/pointer begin event. */
603
+ onPointerDown(id, x, y) {
604
+ pointerDown(this.pointer, x, y);
605
+ this._addOrUpdateTouch(id, x, y, true);
606
+ }
607
+ /** Feed a touch/pointer move event. */
608
+ onPointerMove(id, x, y) {
609
+ pointerMove(this.pointer, x, y);
610
+ this._addOrUpdateTouch(id, x, y, true);
611
+ }
612
+ /** Feed a touch/pointer end event. */
613
+ onPointerUp(id, x, y) {
614
+ pointerUp(this.pointer, x, y);
615
+ this._removeTouch(id);
616
+ }
617
+ /** Access the gamepad state at `index`. Allocates on first access. */
618
+ gamepad(index) {
619
+ let pad = this._gamepads[index];
620
+ if (!pad) {
621
+ pad = createGamepadState(index);
622
+ this._gamepads[index] = pad;
623
+ }
624
+ return pad;
625
+ }
626
+ /** Dead zone in [0, 1). Axes at or below this magnitude read as 0. */
627
+ setGamepadDeadZone(deadZone) {
628
+ this._gamepadDeadZone = Math.max(0, Math.min(.95, deadZone));
629
+ }
630
+ /** Install a remap table for a specific gamepad index. Pass `null` to clear. */
631
+ setGamepadRemap(index, table) {
632
+ if (table === null) this._gamepadRemaps.delete(index);
633
+ else this._gamepadRemaps.set(index, table);
634
+ }
635
+ /** Poll all gamepads from the given source, applying remap + dead zones. */
636
+ pollGamepads(source) {
637
+ const snapshots = source.poll();
638
+ for (let i = 0; i < snapshots.length; i++) {
639
+ const pad = this.gamepad(i);
640
+ const snap = snapshots[i] ?? null;
641
+ updateGamepadFromSnapshot(pad, snap !== null ? this._gamepadRemaps.get(i) ? applyRemap(snap, this._gamepadRemaps.get(i)) : snap : null, this._gamepadDeadZone);
642
+ }
643
+ }
644
+ _addOrUpdateTouch(id, x, y, isDown) {
645
+ let touch = this._touchMap.get(id);
646
+ if (!touch) {
647
+ touch = {
648
+ id,
649
+ position: {
650
+ x,
651
+ y
652
+ },
653
+ isDown
654
+ };
655
+ this._touchMap.set(id, touch);
656
+ this.touches.push(touch);
657
+ } else {
658
+ touch.position.x = x;
659
+ touch.position.y = y;
660
+ touch.isDown = isDown;
661
+ }
662
+ }
663
+ _removeTouch(id) {
664
+ const touch = this._touchMap.get(id);
665
+ if (!touch) return;
666
+ this._touchMap.delete(id);
667
+ const idx = this.touches.indexOf(touch);
668
+ if (idx !== -1) this.touches.splice(idx, 1);
669
+ }
296
670
  };
297
-
298
- // src/gamepad-source.ts
299
- function createWebGamepadSource(nav) {
300
- const source = nav ?? (typeof globalThis === "undefined" ? void 0 : globalThis.navigator);
301
- return {
302
- poll() {
303
- if (!source?.getGamepads) return [];
304
- const pads = source.getGamepads();
305
- const out = new Array(pads.length);
306
- for (let i = 0; i < pads.length; i++) {
307
- const p = pads[i];
308
- if (!p || !p.connected) {
309
- out[i] = null;
310
- continue;
311
- }
312
- out[i] = webGamepadToSnapshot(p);
313
- }
314
- return out;
315
- }
316
- };
317
- }
318
- function webGamepadToSnapshot(p) {
319
- const buttons = new Array(p.buttons.length);
320
- for (let i = 0; i < p.buttons.length; i++) {
321
- const b = p.buttons[i];
322
- buttons[i] = { pressed: b?.pressed ?? false, value: b?.value ?? 0 };
323
- }
324
- const axes = new Array(p.axes.length);
325
- for (let i = 0; i < p.axes.length; i++) axes[i] = p.axes[i] ?? 0;
326
- return { id: p.id, index: p.index, timestamp: p.timestamp, buttons, axes };
327
- }
328
-
329
- // src/gestures.ts
330
- import { Signal } from "@flare-engine/events";
331
- var DEFAULT_TAP_MAX_DURATION = 0.3;
332
- var DEFAULT_TAP_MAX_DISTANCE = 20;
333
- var DEFAULT_LONG_PRESS_MIN_DURATION = 0.5;
334
- var DEFAULT_SWIPE_MIN_DISTANCE = 50;
335
- var DEFAULT_SWIPE_MAX_DURATION = 0.4;
336
- var GestureDetector = class {
337
- onTap = new Signal();
338
- onLongPress = new Signal();
339
- onSwipe = new Signal();
340
- onPinch = new Signal();
341
- tapMaxDuration;
342
- tapMaxDistance;
343
- longPressMinDuration;
344
- swipeMinDistance;
345
- swipeMaxDuration;
346
- // Single-touch tracking
347
- _startPos = { x: 0, y: 0 };
348
- _startTime = 0;
349
- _isTracking = false;
350
- _longPressFired = false;
351
- // Pinch tracking
352
- _pinchStartDistance = 0;
353
- _pinchTouches = null;
354
- constructor(config) {
355
- this.tapMaxDuration = config?.tapMaxDuration ?? DEFAULT_TAP_MAX_DURATION;
356
- this.tapMaxDistance = config?.tapMaxDistance ?? DEFAULT_TAP_MAX_DISTANCE;
357
- this.longPressMinDuration = config?.longPressMinDuration ?? DEFAULT_LONG_PRESS_MIN_DURATION;
358
- this.swipeMinDistance = config?.swipeMinDistance ?? DEFAULT_SWIPE_MIN_DISTANCE;
359
- this.swipeMaxDuration = config?.swipeMaxDuration ?? DEFAULT_SWIPE_MAX_DURATION;
360
- }
361
- /** Feed a pointer-down event. */
362
- pointerDown(x, y) {
363
- this._startPos = { x, y };
364
- this._startTime = 0;
365
- this._isTracking = true;
366
- this._longPressFired = false;
367
- }
368
- /** Feed a pointer-up event. */
369
- pointerUp(x, y, elapsedSinceDown) {
370
- if (!this._isTracking) return;
371
- this._isTracking = false;
372
- const dx = x - this._startPos.x;
373
- const dy = y - this._startPos.y;
374
- const dist = Math.sqrt(dx * dx + dy * dy);
375
- const duration = elapsedSinceDown;
376
- if (dist >= this.swipeMinDistance && duration <= this.swipeMaxDuration) {
377
- const absDx = Math.abs(dx);
378
- const absDy = Math.abs(dy);
379
- let direction;
380
- if (absDx > absDy) {
381
- direction = dx > 0 ? "right" : "left";
382
- } else {
383
- direction = dy > 0 ? "down" : "up";
384
- }
385
- this.onSwipe.emit({ direction, distance: dist, duration });
386
- return;
387
- }
388
- if (dist <= this.tapMaxDistance && duration <= this.tapMaxDuration) {
389
- this.onTap.emit({ x, y });
390
- return;
391
- }
392
- }
393
- /**
394
- * Update gesture tracking. Call each frame with dt.
395
- * @param dt Delta time in seconds.
396
- * @param currentX Current pointer X.
397
- * @param currentY Current pointer Y.
398
- */
399
- update(dt, currentX, currentY) {
400
- if (!this._isTracking) return;
401
- this._startTime += dt;
402
- if (!this._longPressFired && this._startTime >= this.longPressMinDuration) {
403
- const dx = currentX - this._startPos.x;
404
- const dy = currentY - this._startPos.y;
405
- const dist = Math.sqrt(dx * dx + dy * dy);
406
- if (dist <= this.tapMaxDistance) {
407
- this._longPressFired = true;
408
- this.onLongPress.emit({ x: currentX, y: currentY });
409
- }
410
- }
411
- }
412
- /**
413
- * Feed a two-finger pinch update. Call when two touches are active.
414
- * @param t1 First touch position.
415
- * @param t2 Second touch position.
416
- */
417
- pinchUpdate(t1, t2) {
418
- const dx = t2.x - t1.x;
419
- const dy = t2.y - t1.y;
420
- const dist = Math.sqrt(dx * dx + dy * dy);
421
- if (this._pinchTouches === null) {
422
- this._pinchStartDistance = dist;
423
- this._pinchTouches = [{ ...t1 }, { ...t2 }];
424
- return;
425
- }
426
- if (this._pinchStartDistance > 0) {
427
- const scale = dist / this._pinchStartDistance;
428
- const center = {
429
- x: (t1.x + t2.x) / 2,
430
- y: (t1.y + t2.y) / 2
431
- };
432
- this.onPinch.emit({ scale, center });
433
- }
434
- }
435
- /** Call when pinch ends (fewer than 2 touches). */
436
- pinchEnd() {
437
- this._pinchTouches = null;
438
- this._pinchStartDistance = 0;
439
- }
440
- };
441
-
442
- // src/virtual-joystick.ts
443
- var DEFAULT_DEAD_ZONE = 10;
444
- var DEFAULT_MAX_RADIUS = 50;
671
+ //#endregion
672
+ //#region src/virtual-joystick.ts
673
+ const DEFAULT_DEAD_ZONE = 10;
674
+ const DEFAULT_MAX_RADIUS = 50;
675
+ /** Create a default joystick state. */
445
676
  function createJoystickState() {
446
- return {
447
- active: false,
448
- direction: { x: 0, y: 0 },
449
- magnitude: 0,
450
- offset: { x: 0, y: 0 }
451
- };
677
+ return {
678
+ active: false,
679
+ direction: {
680
+ x: 0,
681
+ y: 0
682
+ },
683
+ magnitude: 0,
684
+ offset: {
685
+ x: 0,
686
+ y: 0
687
+ }
688
+ };
452
689
  }
690
+ /**
691
+ * Update joystick state from a touch event.
692
+ *
693
+ * @param state The joystick state to update.
694
+ * @param centerX The joystick center X in screen pixels.
695
+ * @param centerY The joystick center Y in screen pixels.
696
+ * @param touchX The current touch X in screen pixels.
697
+ * @param touchY The current touch Y in screen pixels.
698
+ * @param config Joystick configuration.
699
+ */
453
700
  function updateJoystick(state, centerX, centerY, touchX, touchY, config) {
454
- const deadZone = config?.deadZone ?? DEFAULT_DEAD_ZONE;
455
- const maxRadius = config?.maxRadius ?? DEFAULT_MAX_RADIUS;
456
- const dx = touchX - centerX;
457
- const dy = touchY - centerY;
458
- const dist = Math.sqrt(dx * dx + dy * dy);
459
- state.active = true;
460
- if (dist < deadZone) {
461
- state.direction.x = 0;
462
- state.direction.y = 0;
463
- state.magnitude = 0;
464
- state.offset.x = dx;
465
- state.offset.y = dy;
466
- return;
467
- }
468
- const clampedDist = Math.min(dist, maxRadius);
469
- const normalizedDist = (clampedDist - deadZone) / (maxRadius - deadZone);
470
- const invDist = 1 / dist;
471
- state.direction.x = dx * invDist;
472
- state.direction.y = dy * invDist;
473
- state.magnitude = Math.min(normalizedDist, 1);
474
- const scale = clampedDist / dist;
475
- state.offset.x = dx * scale;
476
- state.offset.y = dy * scale;
701
+ const deadZone = config?.deadZone ?? DEFAULT_DEAD_ZONE;
702
+ const maxRadius = config?.maxRadius ?? DEFAULT_MAX_RADIUS;
703
+ const dx = touchX - centerX;
704
+ const dy = touchY - centerY;
705
+ const dist = Math.sqrt(dx * dx + dy * dy);
706
+ state.active = true;
707
+ if (dist < deadZone) {
708
+ state.direction.x = 0;
709
+ state.direction.y = 0;
710
+ state.magnitude = 0;
711
+ state.offset.x = dx;
712
+ state.offset.y = dy;
713
+ return;
714
+ }
715
+ const clampedDist = Math.min(dist, maxRadius);
716
+ const normalizedDist = (clampedDist - deadZone) / (maxRadius - deadZone);
717
+ const invDist = 1 / dist;
718
+ state.direction.x = dx * invDist;
719
+ state.direction.y = dy * invDist;
720
+ state.magnitude = Math.min(normalizedDist, 1);
721
+ const scale = clampedDist / dist;
722
+ state.offset.x = dx * scale;
723
+ state.offset.y = dy * scale;
477
724
  }
725
+ /** Reset joystick state when touch is released. */
478
726
  function releaseJoystick(state) {
479
- state.active = false;
480
- state.direction.x = 0;
481
- state.direction.y = 0;
482
- state.magnitude = 0;
483
- state.offset.x = 0;
484
- state.offset.y = 0;
727
+ state.active = false;
728
+ state.direction.x = 0;
729
+ state.direction.y = 0;
730
+ state.magnitude = 0;
731
+ state.offset.x = 0;
732
+ state.offset.y = 0;
485
733
  }
734
+ //#endregion
735
+ export { GamepadAxis, GamepadButton, GestureDetector, InputManager, applyRemap, attachKeyboardListeners, createButtonState, createGamepadState, createIdentityRemap, createJoystickState, createKeyboard, createPointer, createWebGamepadSource, getAxis, isActionDown, isButtonDown, loadRemap, pointerDown, pointerMove, pointerUp, pressButton, releaseButton, releaseJoystick, resetButtonFrame, resetGamepadFrame, resetKeyboardFrame, resetPointerFrame, saveRemap, updateGamepadFromSnapshot, updateJoystick, wasActionPressed, wasActionReleased, wasButtonPressed, wasButtonReleased };
486
736
 
487
- // src/virtual-button.ts
488
- function createButtonState() {
489
- return {
490
- isPressed: false,
491
- justPressed: false,
492
- justReleased: false
493
- };
494
- }
495
- function resetButtonFrame(state) {
496
- state.justPressed = false;
497
- state.justReleased = false;
498
- }
499
- function pressButton(state) {
500
- if (!state.isPressed) {
501
- state.isPressed = true;
502
- state.justPressed = true;
503
- }
504
- }
505
- function releaseButton(state) {
506
- if (state.isPressed) {
507
- state.isPressed = false;
508
- state.justReleased = true;
509
- }
510
- }
511
- export {
512
- GamepadAxis,
513
- GamepadButton,
514
- GestureDetector,
515
- InputManager,
516
- applyRemap,
517
- createButtonState,
518
- createGamepadState,
519
- createIdentityRemap,
520
- createJoystickState,
521
- createPointer,
522
- createWebGamepadSource,
523
- getAxis,
524
- isButtonDown,
525
- loadRemap,
526
- pointerDown,
527
- pointerMove,
528
- pointerUp,
529
- pressButton,
530
- releaseButton,
531
- releaseJoystick,
532
- resetButtonFrame,
533
- resetGamepadFrame,
534
- resetPointerFrame,
535
- saveRemap,
536
- updateGamepadFromSnapshot,
537
- updateJoystick,
538
- wasButtonPressed,
539
- wasButtonReleased
540
- };
541
737
  //# sourceMappingURL=index.mjs.map