@forgeax/engine-input 0.1.33 → 0.1.35

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
@@ -506,6 +506,234 @@ function cloneSwipeHistory(src) {
506
506
  return out;
507
507
  }
508
508
 
509
+ // src/input-snapshot.ts
510
+ function createEmptyInputBackendSample() {
511
+ return {
512
+ downKeys: /* @__PURE__ */ new Set(),
513
+ upKeys: /* @__PURE__ */ new Set(),
514
+ buttons: [false, false, false],
515
+ movementX: 0,
516
+ movementY: 0,
517
+ wheelDelta: 0,
518
+ focused: true,
519
+ pointerLocked: false
520
+ };
521
+ }
522
+ function emptyGamepadReader() {
523
+ return Object.freeze({
524
+ connected: false,
525
+ standardMapping: false,
526
+ button(_b) {
527
+ return false;
528
+ },
529
+ buttonValue(_b) {
530
+ return 0;
531
+ },
532
+ justPressed(_b) {
533
+ return false;
534
+ },
535
+ justReleased(_b) {
536
+ return false;
537
+ },
538
+ axis(_a) {
539
+ return 0;
540
+ }
541
+ });
542
+ }
543
+ function buildGamepadReader(slot) {
544
+ if (!slot.standardMapping) {
545
+ return Object.freeze({
546
+ connected: true,
547
+ standardMapping: false,
548
+ button: emptyGamepadReader().button,
549
+ buttonValue: emptyGamepadReader().buttonValue,
550
+ justPressed: emptyGamepadReader().justPressed,
551
+ justReleased: emptyGamepadReader().justReleased,
552
+ axis: emptyGamepadReader().axis
553
+ });
554
+ }
555
+ return Object.freeze({
556
+ connected: true,
557
+ standardMapping: true,
558
+ button(b) {
559
+ return slot.pressed.has(b);
560
+ },
561
+ buttonValue(b) {
562
+ return slot.buttonValues.get(b) ?? 0;
563
+ },
564
+ justPressed(b) {
565
+ return slot.justPressed.has(b);
566
+ },
567
+ justReleased(b) {
568
+ return slot.justReleased.has(b);
569
+ },
570
+ axis(a) {
571
+ return slot.axes[a];
572
+ }
573
+ });
574
+ }
575
+ function snapshotFromSample(sample, actionStates, inputMap, previousSnapshot) {
576
+ const heldKeys = new Set(sample.downKeys);
577
+ const upEdges = new Set(sample.upKeys);
578
+ const justPressedKeys = new Set(sample.pressedKeys);
579
+ if (sample.pressedKeys === void 0) {
580
+ for (const key of heldKeys) {
581
+ if (!previousSnapshot?.keyboard.down(key)) justPressedKeys.add(key);
582
+ }
583
+ }
584
+ const heldCodes = new Set(sample.downCodes ?? []);
585
+ const upCodeEdges = new Set(sample.upCodes ?? []);
586
+ const justPressedCodes = new Set(sample.pressedCodes);
587
+ if (sample.pressedCodes === void 0) {
588
+ for (const code of heldCodes) {
589
+ if (!previousSnapshot?.keyboard.downCode(code)) justPressedCodes.add(code);
590
+ }
591
+ }
592
+ const buttons = [
593
+ sample.buttons[0],
594
+ sample.buttons[1],
595
+ sample.buttons[2]
596
+ ];
597
+ const justPressedButtons = sample.pressedButtons === void 0 ? [
598
+ buttons[0] && (previousSnapshot === void 0 || !previousSnapshot.mouse.button(0)),
599
+ buttons[1] && (previousSnapshot === void 0 || !previousSnapshot.mouse.button(1)),
600
+ buttons[2] && (previousSnapshot === void 0 || !previousSnapshot.mouse.button(2))
601
+ ] : [sample.pressedButtons[0], sample.pressedButtons[1], sample.pressedButtons[2]];
602
+ const justReleasedButtons = sample.releasedButtons === void 0 ? [
603
+ sample.focusReset !== true && previousSnapshot?.mouse.button(0) === true && !buttons[0],
604
+ sample.focusReset !== true && previousSnapshot?.mouse.button(1) === true && !buttons[1],
605
+ sample.focusReset !== true && previousSnapshot?.mouse.button(2) === true && !buttons[2]
606
+ ] : [sample.releasedButtons[0], sample.releasedButtons[1], sample.releasedButtons[2]];
607
+ const movementDelta = Object.freeze({ x: sample.movementX, y: sample.movementY });
608
+ const mousePosition = sample.mouseX === void 0 || sample.mouseY === void 0 ? void 0 : Object.freeze({ x: sample.mouseX, y: sample.mouseY });
609
+ const wheelDelta = sample.wheelDelta;
610
+ const gamepadSlots = sample.gamepads ?? [];
611
+ const caps = sample.capabilities ?? Object.freeze({ gamepad: false, pointer: false });
612
+ const pointerEvts = sample.pointerEvents ?? [];
613
+ const gesture = sample.gestures ?? IDENTITY_GESTURE;
614
+ const gestureEvts = sample.gestureEvents ?? [];
615
+ const _virtualAxes = sample.virtualAxes ?? [];
616
+ const virtualAxesMap = /* @__PURE__ */ new Map();
617
+ for (const va of _virtualAxes) {
618
+ virtualAxesMap.set(va.name, va);
619
+ }
620
+ const gamepadSlotMap = /* @__PURE__ */ new Map();
621
+ for (const slot of gamepadSlots) {
622
+ gamepadSlotMap.set(slot.index, slot);
623
+ }
624
+ const actionMap = /* @__PURE__ */ new Map();
625
+ if (actionStates) {
626
+ for (const a of actionStates) {
627
+ actionMap.set(a.action, a);
628
+ }
629
+ }
630
+ function emptyActionReader() {
631
+ return Object.freeze({
632
+ isPressed: () => false,
633
+ justPressed: () => false,
634
+ justReleased: () => false,
635
+ strength: 0
636
+ });
637
+ }
638
+ const snapshot = {
639
+ keyboard: {
640
+ down(key) {
641
+ return heldKeys.has(key);
642
+ },
643
+ justPressed(key) {
644
+ return justPressedKeys.has(key);
645
+ },
646
+ up(key) {
647
+ return upEdges.has(key);
648
+ },
649
+ downCode(code) {
650
+ return heldCodes.has(code);
651
+ },
652
+ justPressedCode(code) {
653
+ return justPressedCodes.has(code);
654
+ },
655
+ upCode(code) {
656
+ return upCodeEdges.has(code);
657
+ }
658
+ },
659
+ mouse: {
660
+ position: mousePosition,
661
+ movementDelta,
662
+ pointerLocked: sample.pointerLocked,
663
+ button(i) {
664
+ return buttons[i] === true;
665
+ },
666
+ justPressed(i) {
667
+ return justPressedButtons[i] === true;
668
+ },
669
+ justReleased(i) {
670
+ return justReleasedButtons[i] === true;
671
+ },
672
+ wheelDelta
673
+ },
674
+ gamepad(i) {
675
+ const slot = gamepadSlotMap.get(i);
676
+ if (!slot) return emptyGamepadReader();
677
+ return buildGamepadReader(slot);
678
+ },
679
+ capabilities: caps,
680
+ pointer(id) {
681
+ const entry = (sample.pointers ?? []).find((p) => p.pointerId === id);
682
+ if (!entry) {
683
+ return Object.freeze({
684
+ active: false,
685
+ pointerId: -1,
686
+ x: 0,
687
+ y: 0,
688
+ pressure: 0,
689
+ pointerType: "mouse",
690
+ delta: Object.freeze({ x: 0, y: 0 })
691
+ });
692
+ }
693
+ return Object.freeze({
694
+ ...entry,
695
+ delta: Object.freeze({ x: entry.delta.x, y: entry.delta.y })
696
+ });
697
+ },
698
+ virtualAxis(name) {
699
+ const va = virtualAxesMap.get(name);
700
+ if (!va) return Object.freeze({ x: 0, y: 0 });
701
+ return Object.freeze({ x: va.x, y: va.y });
702
+ },
703
+ action(name) {
704
+ const s = actionMap.get(name);
705
+ if (!s) return emptyActionReader();
706
+ return Object.freeze({
707
+ isPressed: () => s.pressed,
708
+ justPressed: () => s.justPressed,
709
+ justReleased: () => s.justReleased,
710
+ strength: s.strength
711
+ });
712
+ },
713
+ getAxis(neg, pos) {
714
+ if (!actionStates || !inputMap) return 0;
715
+ return getAxis(inputMap, actionStates, neg, pos);
716
+ },
717
+ getVector(negX, posX, negY, posY, opts) {
718
+ if (!actionStates || !inputMap) return { x: 0, y: 0 };
719
+ return getVector(inputMap, actionStates, negX, posX, negY, posY, opts);
720
+ },
721
+ pointerEvents: pointerEvts,
722
+ gesture,
723
+ gestureEvents: gestureEvts
724
+ };
725
+ snapshot._actionStates = actionStates;
726
+ snapshot._inputMap = inputMap;
727
+ return Object.freeze(snapshot);
728
+ }
729
+ function readActionStatesForEdgeDiff(snap) {
730
+ return snap._actionStates;
731
+ }
732
+ function createInputSnapshot() {
733
+ return snapshotFromSample(createEmptyInputBackendSample());
734
+ }
735
+ var INPUT_SNAPSHOT_RESOURCE_KEY = "InputSnapshot";
736
+
509
737
  // src/ui-ownership.ts
510
738
  function contains(root, value) {
511
739
  const NodeCtor = globalThis.Node;
@@ -609,6 +837,7 @@ function attachBrowserInputBackend(canvas, options = {}) {
609
837
  let w3cLocked = false;
610
838
  let providerLocked = false;
611
839
  let gameGate = true;
840
+ let inputAllowed = true;
612
841
  function emitLockError(path, cause) {
613
842
  try {
614
843
  options.onLockError?.({ path, cause });
@@ -685,6 +914,7 @@ function attachBrowserInputBackend(canvas, options = {}) {
685
914
  return typeof doc?.hasFocus === "function" ? doc.hasFocus() : true;
686
915
  }
687
916
  function onKeyDown(ev) {
917
+ if (!inputAllowed) return;
688
918
  if (isUiEvent(ev)) {
689
919
  clear();
690
920
  return;
@@ -700,6 +930,7 @@ function attachBrowserInputBackend(canvas, options = {}) {
700
930
  }
701
931
  }
702
932
  function onKeyUp(ev) {
933
+ if (!inputAllowed) return;
703
934
  if (isUiEvent(ev)) return;
704
935
  heldKeys.delete(ev.key);
705
936
  if (isFocused()) {
@@ -711,6 +942,7 @@ function attachBrowserInputBackend(canvas, options = {}) {
711
942
  }
712
943
  }
713
944
  function onPointerDown(ev) {
945
+ if (!inputAllowed) return;
714
946
  if (isUiEvent(ev)) {
715
947
  clear();
716
948
  return;
@@ -770,6 +1002,7 @@ function attachBrowserInputBackend(canvas, options = {}) {
770
1002
  }
771
1003
  }
772
1004
  function onPointerUp(ev) {
1005
+ if (!inputAllowed) return;
773
1006
  if (isUiEvent(ev)) return;
774
1007
  if (ev.pointerType === "mouse") {
775
1008
  if (ev.button === 0 || ev.button === 1 || ev.button === 2) {
@@ -794,6 +1027,7 @@ function attachBrowserInputBackend(canvas, options = {}) {
794
1027
  }
795
1028
  }
796
1029
  function onPointerMove(ev) {
1030
+ if (!inputAllowed) return;
797
1031
  if (isUiEvent(ev)) return;
798
1032
  if (ev.pointerType === "mouse") {
799
1033
  mvx += ev.movementX;
@@ -820,6 +1054,7 @@ function attachBrowserInputBackend(canvas, options = {}) {
820
1054
  }
821
1055
  }
822
1056
  function onPointerCancel(ev) {
1057
+ if (!inputAllowed) return;
823
1058
  if (isUiEvent(ev)) return;
824
1059
  const entry = pointerMap.get(ev.pointerId);
825
1060
  if (entry) {
@@ -843,6 +1078,7 @@ function attachBrowserInputBackend(canvas, options = {}) {
843
1078
  }
844
1079
  }
845
1080
  function onWheel(ev) {
1081
+ if (!inputAllowed) return;
846
1082
  if (isUiEvent(ev)) {
847
1083
  clear();
848
1084
  return;
@@ -925,6 +1161,7 @@ function attachBrowserInputBackend(canvas, options = {}) {
925
1161
  canvas.style.touchAction = "none";
926
1162
  }
927
1163
  function onCanvasClick() {
1164
+ if (!inputAllowed) return;
928
1165
  if (!gameGate) return;
929
1166
  if (options.pointerLockAllowed && !options.pointerLockAllowed()) return;
930
1167
  if (options.lockProvider) {
@@ -958,6 +1195,10 @@ function attachBrowserInputBackend(canvas, options = {}) {
958
1195
  }
959
1196
  safeAdd(canvas, "click", onCanvasClick);
960
1197
  function sample() {
1198
+ if (!inputAllowed) {
1199
+ clear();
1200
+ return createEmptyInputBackendSample();
1201
+ }
961
1202
  let gamepads;
962
1203
  if (gamepadAvailable) {
963
1204
  try {
@@ -1074,6 +1315,10 @@ function attachBrowserInputBackend(canvas, options = {}) {
1074
1315
  releaseProviderLock();
1075
1316
  }
1076
1317
  }
1318
+ function setInputAllowed(allowed) {
1319
+ inputAllowed = allowed;
1320
+ clear();
1321
+ }
1077
1322
  function clear() {
1078
1323
  heldKeys.clear();
1079
1324
  upEdges.clear();
@@ -1124,243 +1369,23 @@ function attachBrowserInputBackend(canvas, options = {}) {
1124
1369
  canvas.style.touchAction = _prevTouchAction;
1125
1370
  }
1126
1371
  }
1127
- const backend = { sample, clear, detach, setPointerLockAllowed };
1372
+ const backend = {
1373
+ sample,
1374
+ clear,
1375
+ detach,
1376
+ setPointerLockAllowed,
1377
+ setInputAllowed
1378
+ };
1128
1379
  const handle = Object.assign(detach, { backend });
1129
1380
  return handle;
1130
1381
  }
1131
1382
 
1132
- // src/input-snapshot.ts
1133
- function createEmptyInputBackendSample() {
1134
- return {
1135
- downKeys: /* @__PURE__ */ new Set(),
1136
- upKeys: /* @__PURE__ */ new Set(),
1137
- buttons: [false, false, false],
1138
- movementX: 0,
1139
- movementY: 0,
1140
- wheelDelta: 0,
1141
- focused: true,
1142
- pointerLocked: false
1143
- };
1144
- }
1145
- function emptyGamepadReader() {
1146
- return Object.freeze({
1147
- connected: false,
1148
- standardMapping: false,
1149
- button(_b) {
1150
- return false;
1151
- },
1152
- buttonValue(_b) {
1153
- return 0;
1154
- },
1155
- justPressed(_b) {
1156
- return false;
1157
- },
1158
- justReleased(_b) {
1159
- return false;
1160
- },
1161
- axis(_a) {
1162
- return 0;
1163
- }
1164
- });
1165
- }
1166
- function buildGamepadReader(slot) {
1167
- if (!slot.standardMapping) {
1168
- return Object.freeze({
1169
- connected: true,
1170
- standardMapping: false,
1171
- button: emptyGamepadReader().button,
1172
- buttonValue: emptyGamepadReader().buttonValue,
1173
- justPressed: emptyGamepadReader().justPressed,
1174
- justReleased: emptyGamepadReader().justReleased,
1175
- axis: emptyGamepadReader().axis
1176
- });
1177
- }
1178
- return Object.freeze({
1179
- connected: true,
1180
- standardMapping: true,
1181
- button(b) {
1182
- return slot.pressed.has(b);
1183
- },
1184
- buttonValue(b) {
1185
- return slot.buttonValues.get(b) ?? 0;
1186
- },
1187
- justPressed(b) {
1188
- return slot.justPressed.has(b);
1189
- },
1190
- justReleased(b) {
1191
- return slot.justReleased.has(b);
1192
- },
1193
- axis(a) {
1194
- return slot.axes[a];
1195
- }
1196
- });
1197
- }
1198
- function snapshotFromSample(sample, actionStates, inputMap, previousSnapshot) {
1199
- const heldKeys = new Set(sample.downKeys);
1200
- const upEdges = new Set(sample.upKeys);
1201
- const justPressedKeys = new Set(sample.pressedKeys);
1202
- if (sample.pressedKeys === void 0) {
1203
- for (const key of heldKeys) {
1204
- if (!previousSnapshot?.keyboard.down(key)) justPressedKeys.add(key);
1205
- }
1206
- }
1207
- const heldCodes = new Set(sample.downCodes ?? []);
1208
- const upCodeEdges = new Set(sample.upCodes ?? []);
1209
- const justPressedCodes = new Set(sample.pressedCodes);
1210
- if (sample.pressedCodes === void 0) {
1211
- for (const code of heldCodes) {
1212
- if (!previousSnapshot?.keyboard.downCode(code)) justPressedCodes.add(code);
1213
- }
1214
- }
1215
- const buttons = [
1216
- sample.buttons[0],
1217
- sample.buttons[1],
1218
- sample.buttons[2]
1219
- ];
1220
- const justPressedButtons = sample.pressedButtons === void 0 ? [
1221
- buttons[0] && (previousSnapshot === void 0 || !previousSnapshot.mouse.button(0)),
1222
- buttons[1] && (previousSnapshot === void 0 || !previousSnapshot.mouse.button(1)),
1223
- buttons[2] && (previousSnapshot === void 0 || !previousSnapshot.mouse.button(2))
1224
- ] : [sample.pressedButtons[0], sample.pressedButtons[1], sample.pressedButtons[2]];
1225
- const justReleasedButtons = sample.releasedButtons === void 0 ? [
1226
- sample.focusReset !== true && previousSnapshot?.mouse.button(0) === true && !buttons[0],
1227
- sample.focusReset !== true && previousSnapshot?.mouse.button(1) === true && !buttons[1],
1228
- sample.focusReset !== true && previousSnapshot?.mouse.button(2) === true && !buttons[2]
1229
- ] : [sample.releasedButtons[0], sample.releasedButtons[1], sample.releasedButtons[2]];
1230
- const movementDelta = Object.freeze({ x: sample.movementX, y: sample.movementY });
1231
- const mousePosition = sample.mouseX === void 0 || sample.mouseY === void 0 ? void 0 : Object.freeze({ x: sample.mouseX, y: sample.mouseY });
1232
- const wheelDelta = sample.wheelDelta;
1233
- const gamepadSlots = sample.gamepads ?? [];
1234
- const caps = sample.capabilities ?? Object.freeze({ gamepad: false, pointer: false });
1235
- const pointerEvts = sample.pointerEvents ?? [];
1236
- const gesture = sample.gestures ?? IDENTITY_GESTURE;
1237
- const gestureEvts = sample.gestureEvents ?? [];
1238
- const _virtualAxes = sample.virtualAxes ?? [];
1239
- const virtualAxesMap = /* @__PURE__ */ new Map();
1240
- for (const va of _virtualAxes) {
1241
- virtualAxesMap.set(va.name, va);
1242
- }
1243
- const gamepadSlotMap = /* @__PURE__ */ new Map();
1244
- for (const slot of gamepadSlots) {
1245
- gamepadSlotMap.set(slot.index, slot);
1246
- }
1247
- const actionMap = /* @__PURE__ */ new Map();
1248
- if (actionStates) {
1249
- for (const a of actionStates) {
1250
- actionMap.set(a.action, a);
1251
- }
1252
- }
1253
- function emptyActionReader() {
1254
- return Object.freeze({
1255
- isPressed: () => false,
1256
- justPressed: () => false,
1257
- justReleased: () => false,
1258
- strength: 0
1259
- });
1260
- }
1261
- const snapshot = {
1262
- keyboard: {
1263
- down(key) {
1264
- return heldKeys.has(key);
1265
- },
1266
- justPressed(key) {
1267
- return justPressedKeys.has(key);
1268
- },
1269
- up(key) {
1270
- return upEdges.has(key);
1271
- },
1272
- downCode(code) {
1273
- return heldCodes.has(code);
1274
- },
1275
- justPressedCode(code) {
1276
- return justPressedCodes.has(code);
1277
- },
1278
- upCode(code) {
1279
- return upCodeEdges.has(code);
1280
- }
1281
- },
1282
- mouse: {
1283
- position: mousePosition,
1284
- movementDelta,
1285
- pointerLocked: sample.pointerLocked,
1286
- button(i) {
1287
- return buttons[i] === true;
1288
- },
1289
- justPressed(i) {
1290
- return justPressedButtons[i] === true;
1291
- },
1292
- justReleased(i) {
1293
- return justReleasedButtons[i] === true;
1294
- },
1295
- wheelDelta
1296
- },
1297
- gamepad(i) {
1298
- const slot = gamepadSlotMap.get(i);
1299
- if (!slot) return emptyGamepadReader();
1300
- return buildGamepadReader(slot);
1301
- },
1302
- capabilities: caps,
1303
- pointer(id) {
1304
- const entry = (sample.pointers ?? []).find((p) => p.pointerId === id);
1305
- if (!entry) {
1306
- return Object.freeze({
1307
- active: false,
1308
- pointerId: -1,
1309
- x: 0,
1310
- y: 0,
1311
- pressure: 0,
1312
- pointerType: "mouse",
1313
- delta: Object.freeze({ x: 0, y: 0 })
1314
- });
1315
- }
1316
- return Object.freeze({
1317
- ...entry,
1318
- delta: Object.freeze({ x: entry.delta.x, y: entry.delta.y })
1319
- });
1320
- },
1321
- virtualAxis(name) {
1322
- const va = virtualAxesMap.get(name);
1323
- if (!va) return Object.freeze({ x: 0, y: 0 });
1324
- return Object.freeze({ x: va.x, y: va.y });
1325
- },
1326
- action(name) {
1327
- const s = actionMap.get(name);
1328
- if (!s) return emptyActionReader();
1329
- return Object.freeze({
1330
- isPressed: () => s.pressed,
1331
- justPressed: () => s.justPressed,
1332
- justReleased: () => s.justReleased,
1333
- strength: s.strength
1334
- });
1335
- },
1336
- getAxis(neg, pos) {
1337
- if (!actionStates || !inputMap) return 0;
1338
- return getAxis(inputMap, actionStates, neg, pos);
1339
- },
1340
- getVector(negX, posX, negY, posY, opts) {
1341
- if (!actionStates || !inputMap) return { x: 0, y: 0 };
1342
- return getVector(inputMap, actionStates, negX, posX, negY, posY, opts);
1343
- },
1344
- pointerEvents: pointerEvts,
1345
- gesture,
1346
- gestureEvents: gestureEvts
1347
- };
1348
- snapshot._actionStates = actionStates;
1349
- snapshot._inputMap = inputMap;
1350
- return Object.freeze(snapshot);
1351
- }
1352
- function readActionStatesForEdgeDiff(snap) {
1353
- return snap._actionStates;
1354
- }
1355
- function createInputSnapshot() {
1356
- return snapshotFromSample(createEmptyInputBackendSample());
1357
- }
1358
- var INPUT_SNAPSHOT_RESOURCE_KEY = "InputSnapshot";
1359
-
1360
1383
  // src/canvas-input-boundary.ts
1361
1384
  function createCanvasInputBoundary(source) {
1362
1385
  let active = "editor";
1363
1386
  let gamePointerLockAllowed = false;
1387
+ let editorInputAllowed = true;
1388
+ let gameInputAllowed = true;
1364
1389
  source.setPointerLockAllowed?.(false);
1365
1390
  const empty = () => createEmptyInputBackendSample();
1366
1391
  const clear = () => {
@@ -1377,6 +1402,11 @@ function createCanvasInputBoundary(source) {
1377
1402
  source.setPointerLockAllowed?.(false);
1378
1403
  }
1379
1404
  },
1405
+ setInputAllowed: (allowed) => {
1406
+ if (consumer === "game") gameInputAllowed = allowed;
1407
+ else editorInputAllowed = allowed;
1408
+ source.setInputAllowed?.(active === "game" ? gameInputAllowed : editorInputAllowed);
1409
+ },
1380
1410
  clear: () => {
1381
1411
  if (consumer === active) clear();
1382
1412
  },
@@ -1387,10 +1417,12 @@ function createCanvasInputBoundary(source) {
1387
1417
  clear();
1388
1418
  active = "game";
1389
1419
  source.setPointerLockAllowed?.(gamePointerLockAllowed);
1420
+ source.setInputAllowed?.(gameInputAllowed);
1390
1421
  };
1391
1422
  const revokeGame = () => {
1392
1423
  clear();
1393
1424
  active = "editor";
1425
+ source.setInputAllowed?.(editorInputAllowed);
1394
1426
  };
1395
1427
  return {
1396
1428
  editor: routed("editor"),
@@ -1459,6 +1491,7 @@ function normalizeKey(value) {
1459
1491
  }
1460
1492
  function makeCompositeBackend(inner, options) {
1461
1493
  let yieldToHuman = options?.yieldToHuman ?? true;
1494
+ let inputAllowed = true;
1462
1495
  let leaseRevoked = false;
1463
1496
  let leaseGeneration = 0;
1464
1497
  const heldKeys = /* @__PURE__ */ new Set();
@@ -1478,6 +1511,25 @@ function makeCompositeBackend(inner, options) {
1478
1511
  }
1479
1512
  function sample() {
1480
1513
  const base = inner.sample();
1514
+ if (!inputAllowed) {
1515
+ clearInjected();
1516
+ return {
1517
+ ...base,
1518
+ downKeys: /* @__PURE__ */ new Set(),
1519
+ upKeys: /* @__PURE__ */ new Set(),
1520
+ downCodes: /* @__PURE__ */ new Set(),
1521
+ upCodes: /* @__PURE__ */ new Set(),
1522
+ pressedKeys: /* @__PURE__ */ new Set(),
1523
+ pressedCodes: /* @__PURE__ */ new Set(),
1524
+ buttons: [false, false, false],
1525
+ pressedButtons: [false, false, false],
1526
+ releasedButtons: [false, false, false],
1527
+ movementX: 0,
1528
+ movementY: 0,
1529
+ wheelDelta: 0,
1530
+ pointerLocked: false
1531
+ };
1532
+ }
1481
1533
  const downKeys = new Set(base.downKeys);
1482
1534
  const downCodes = base.downCodes === void 0 && heldCodes.size === 0 ? void 0 : new Set(base.downCodes ?? []);
1483
1535
  for (const k of heldKeys) {
@@ -1573,6 +1625,13 @@ function makeCompositeBackend(inner, options) {
1573
1625
  return out;
1574
1626
  }
1575
1627
  const lockGate = inner.setPointerLockAllowed ? { setPointerLockAllowed: (allowed) => inner.setPointerLockAllowed?.(allowed) } : {};
1628
+ const inputGate = {
1629
+ setInputAllowed: (allowed) => {
1630
+ inputAllowed = allowed;
1631
+ inner.setInputAllowed?.(allowed);
1632
+ clearInjected();
1633
+ }
1634
+ };
1576
1635
  const clearInjected = () => {
1577
1636
  upEdges.clear();
1578
1637
  upCodeEdges.clear();
@@ -1609,7 +1668,7 @@ function makeCompositeBackend(inner, options) {
1609
1668
  const createInjectedLease = () => {
1610
1669
  beginInjectedLease();
1611
1670
  const generation = leaseGeneration;
1612
- const active = () => !leaseRevoked && generation === leaseGeneration;
1671
+ const active = () => inputAllowed && !leaseRevoked && generation === leaseGeneration;
1613
1672
  return {
1614
1673
  sample,
1615
1674
  detach: () => {
@@ -1657,6 +1716,7 @@ function makeCompositeBackend(inner, options) {
1657
1716
  return {
1658
1717
  sample,
1659
1718
  ...lockGate,
1719
+ ...inputGate,
1660
1720
  // Teardown belongs to the human backend -- revoke the synthetic lease
1661
1721
  // first, then forward the physical listener teardown. `clear()` is also
1662
1722
  // exposed so an owner can revoke input without detaching the browser.
@@ -1669,7 +1729,7 @@ function makeCompositeBackend(inner, options) {
1669
1729
  inner.detach();
1670
1730
  },
1671
1731
  press(key) {
1672
- if (leaseRevoked) return;
1732
+ if (leaseRevoked || !inputAllowed) return;
1673
1733
  const normalized = normalizeKey(key);
1674
1734
  if (!heldKeys.has(normalized.key)) pressedKeys.add(normalized.key);
1675
1735
  heldKeys.add(normalized.key);
@@ -1679,25 +1739,25 @@ function makeCompositeBackend(inner, options) {
1679
1739
  }
1680
1740
  },
1681
1741
  release(key) {
1682
- if (leaseRevoked) return;
1742
+ if (leaseRevoked || !inputAllowed) return;
1683
1743
  const normalized = normalizeKey(key);
1684
1744
  if (heldKeys.delete(normalized.key)) upEdges.add(normalized.key);
1685
1745
  if (normalized.code !== void 0 && heldCodes.delete(normalized.code))
1686
1746
  upCodeEdges.add(normalized.code);
1687
1747
  },
1688
1748
  setButton(slot, down) {
1689
- if (leaseRevoked) return;
1749
+ if (leaseRevoked || !inputAllowed) return;
1690
1750
  if (down && !buttons[slot]) pressedButtons[slot] = true;
1691
1751
  if (!down && buttons[slot]) releasedButtons[slot] = true;
1692
1752
  buttons[slot] = down;
1693
1753
  },
1694
1754
  addMovement(dx, dy) {
1695
- if (leaseRevoked) return;
1755
+ if (leaseRevoked || !inputAllowed) return;
1696
1756
  mvx += dx;
1697
1757
  mvy += dy;
1698
1758
  },
1699
1759
  addWheel(notches) {
1700
- if (leaseRevoked) return;
1760
+ if (leaseRevoked || !inputAllowed) return;
1701
1761
  wheel += notches;
1702
1762
  },
1703
1763
  clearInjected,