Skip to content

Commit fb47f3b

Browse files
committed
Split game controls and user input defaults with auto-detected devices - PR_26162_040-controls-game-and-user-input-foundation
1 parent da0eb96 commit fb47f3b

37 files changed

Lines changed: 2607 additions & 519 deletions

account/user-controls-page.js

Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import {
88
physicalInputIsAnalog,
99
} from "../src/engine/input/NormalizedInputRegistry.js";
1010

11-
const KEYBOARD_MOUSE_INPUTS = Object.freeze(["KeyW", "KeyA", "KeyS", "KeyD", "Space", "Enter", "Escape", "KeyP", "MouseButton0", "MouseButton2", "MouseWheelUp", "MouseWheelDown", "MouseX", "MouseY"]);
11+
const DEVICE_POLL_INTERVAL_MS = 1200;
12+
const KEYBOARD_INPUTS = Object.freeze(["KeyW", "KeyA", "KeyS", "KeyD", "Space", "Enter", "Escape", "KeyP"]);
13+
const MOUSE_INPUTS = Object.freeze(["MouseButton0", "MouseButton2", "MouseX", "MouseY"]);
1214
const SUPPORTED_CONTROL_TYPES = Object.freeze([
1315
"Keyboard Key",
1416
"Mouse Button",
@@ -122,8 +124,10 @@ export class AccountUserControlsPage {
122124
this.inputService = new InputService({ target: window });
123125
this.profiles = [];
124126
this.editingProfile = null;
127+
this.devicePollingTimer = null;
125128
this.elements = {
126129
addProfile: root.querySelector("[data-account-user-controls-add-profile]"),
130+
defaultList: root.querySelector("[data-account-user-controls-default-list]"),
127131
deviceSelect: root.querySelector("[data-account-user-controls-device]"),
128132
deviceStatus: root.querySelector("[data-account-user-controls-device-status]"),
129133
list: root.querySelector("[data-account-user-controls-list]"),
@@ -138,6 +142,7 @@ export class AccountUserControlsPage {
138142
this.inputService.attach();
139143
this.profiles = this.readProfiles();
140144
this.renderDeviceSelect();
145+
this.renderDefaultFallbacks();
141146
this.renderTypes();
142147
this.renderProfiles();
143148
this.elements.refresh?.addEventListener("click", () => {
@@ -151,6 +156,7 @@ export class AccountUserControlsPage {
151156
this.elements.list?.addEventListener("change", (event) => this.handleListChange(event));
152157
this.elements.list?.addEventListener("input", (event) => this.handleListInput(event));
153158
this.elements.list?.addEventListener("dblclick", (event) => this.handleListDoubleClick(event));
159+
this.startDevicePolling();
154160
}
155161

156162
setStatus(message) {
@@ -171,14 +177,23 @@ export class AccountUserControlsPage {
171177
}
172178

173179
deviceOptions() {
174-
const keyboardMouse = {
175-
controllerId: "keyboard-mouse",
176-
controllerName: "Keyboard/Mouse",
177-
deviceType: "Keyboard/Mouse",
178-
inputs: [...KEYBOARD_MOUSE_INPUTS],
179-
label: "Keyboard/Mouse",
180-
mappingProfile: "Keyboard/Mouse Profile",
181-
value: "keyboard-mouse",
180+
const keyboard = {
181+
controllerId: "generic-keyboard",
182+
controllerName: "Keyboard",
183+
deviceType: "Keyboard",
184+
inputs: [...KEYBOARD_INPUTS],
185+
label: "Keyboard",
186+
mappingProfile: "Keyboard Profile",
187+
value: "keyboard",
188+
};
189+
const mouse = {
190+
controllerId: "generic-mouse",
191+
controllerName: "Mouse",
192+
deviceType: "Mouse",
193+
inputs: [...MOUSE_INPUTS],
194+
label: "Mouse",
195+
mappingProfile: "Mouse Profile",
196+
value: "mouse",
182197
};
183198
const gamepads = this.availableGamepads().map((gamepad) => {
184199
const controllerName = gamepad.id || `Gamepad ${gamepad.index}`;
@@ -192,15 +207,15 @@ export class AccountUserControlsPage {
192207
value: `gamepad-${gamepad.index}`,
193208
};
194209
});
195-
return [keyboardMouse, ...gamepads];
210+
return [keyboard, mouse, ...gamepads];
196211
}
197212

198213
deviceRefreshMessage() {
199214
const gamepadCount = this.availableGamepads().length;
200215
if (gamepadCount > 0) {
201-
return `PASS: Device list refreshed. Keyboard/Mouse and ${gamepadCount} game controller${gamepadCount === 1 ? "" : "s"} available.`;
216+
return `PASS: Keyboard and Mouse available. ${gamepadCount} game controller${gamepadCount === 1 ? "" : "s"} detected automatically.`;
202217
}
203-
return "PASS: Keyboard/Mouse available. To enumerate a game controller, connect it, press a button, allow browser gamepad access, then refresh devices.";
218+
return "PASS: Keyboard and Mouse available. Gamepads auto-detect after browser exposes them.";
204219
}
205220

206221
selectedDevice() {
@@ -301,6 +316,43 @@ export class AccountUserControlsPage {
301316
}));
302317
}
303318

319+
renderDefaultFallbacks() {
320+
if (!this.elements.defaultList) {
321+
return;
322+
}
323+
const systemDefault = this.inputService.getSystemDefaultProfileForDevice("Keyboard/Mouse");
324+
const rows = (systemDefault.inputMappings || [])
325+
.filter((mapping) => KEYBOARD_INPUTS.includes(mapping.physicalInput) || MOUSE_INPUTS.includes(mapping.physicalInput))
326+
.map((mapping) => {
327+
const row = document.createElement("tr");
328+
row.dataset.accountUserControlsDefaultRow = mapping.physicalInput;
329+
const family = MOUSE_INPUTS.includes(mapping.physicalInput) ? "Mouse" : "Keyboard";
330+
row.append(
331+
tableCell(family),
332+
tableCell(mapping.physicalInput),
333+
tableCell(mapping.normalizedInput || mapping.positiveNormalizedInput || mapping.negativeNormalizedInput || "Unassigned"),
334+
tableCell("Visible fallback"),
335+
);
336+
return row;
337+
});
338+
this.elements.defaultList.replaceChildren(...rows);
339+
}
340+
341+
startDevicePolling() {
342+
if (this.devicePollingTimer || typeof window.setInterval !== "function") {
343+
return;
344+
}
345+
this.devicePollingTimer = window.setInterval(() => {
346+
this.renderDeviceSelect();
347+
}, DEVICE_POLL_INTERVAL_MS);
348+
window.addEventListener("pagehide", () => {
349+
if (this.devicePollingTimer) {
350+
window.clearInterval(this.devicePollingTimer);
351+
this.devicePollingTimer = null;
352+
}
353+
}, { once: true });
354+
}
355+
304356
renderProfiles() {
305357
if (!this.elements.list) {
306358
return;

account/user-controls.html

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,25 @@ <h2>Physical Input Mapping</h2>
4040
<button class="btn primary" type="button" data-account-user-controls-save-all>Save</button>
4141
</div>
4242
<p class="status" role="status" data-account-user-controls-device-status>Refresh devices, choose a physical controller, then create a user control profile.</p>
43+
<details class="vertical-accordion" open>
44+
<summary>Default Keyboard/Mouse Fallbacks</summary>
45+
<div class="accordion-body content-stack">
46+
<p class="status">Keyboard and Mouse defaults are visible fallbacks. They are not saved as user-created profiles until you create and save a profile.</p>
47+
<div class="table-wrapper">
48+
<table class="data-table" aria-label="Default keyboard and mouse fallbacks" data-account-user-controls-defaults>
49+
<thead>
50+
<tr>
51+
<th>Input Family</th>
52+
<th>Physical Input</th>
53+
<th>Normalized Control</th>
54+
<th>Source</th>
55+
</tr>
56+
</thead>
57+
<tbody data-account-user-controls-default-list></tbody>
58+
</table>
59+
</div>
60+
</div>
61+
</details>
4362
<div class="table-wrapper">
4463
<table class="data-table" aria-label="Account user control profiles" data-account-user-controls-table>
4564
<thead>
Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,76 @@
1-
 M docs_build/dev/reports/codex_changed_files.txt
2-
M docs_build/dev/reports/codex_review.diff
1+
# git status --short
2+
M account/user-controls-page.js
3+
M account/user-controls.html
4+
M docs_build/dev/reports/coverage_changed_js_guardrail.txt
5+
M docs_build/dev/reports/dependency_gating_report.md
6+
M docs_build/dev/reports/dependency_hydration_reuse_report.md
7+
M docs_build/dev/reports/execution_graph_reuse_report.md
8+
M docs_build/dev/reports/failure_fingerprint_report.md
9+
M docs_build/dev/reports/filesystem_scan_reduction_report.md
10+
M docs_build/dev/reports/incremental_validation_report.md
11+
M docs_build/dev/reports/lane_compilation_report.md
12+
M docs_build/dev/reports/lane_deduplication_report.md
13+
M docs_build/dev/reports/lane_input_validation_report.md
14+
M docs_build/dev/reports/lane_runtime_optimization_report.md
15+
M docs_build/dev/reports/lane_snapshot_report.md
16+
M docs_build/dev/reports/lane_warm_start_report.md
17+
M docs_build/dev/reports/monolith_trigger_removal_report.md
18+
M docs_build/dev/reports/persistent_lane_manifest_report.md
19+
M docs_build/dev/reports/playwright_discovery_ownership_report.md
20+
M docs_build/dev/reports/playwright_discovery_scope_report.md
21+
M docs_build/dev/reports/playwright_structure_audit.md
322
M docs_build/dev/reports/playwright_v8_coverage_report.txt
23+
M docs_build/dev/reports/retry_suppression_report.md
24+
M docs_build/dev/reports/slow_path_pruning_report.md
25+
M docs_build/dev/reports/static_validation_report.md
26+
M docs_build/dev/reports/targeted_file_manifest_report.md
27+
M docs_build/dev/reports/test_cleanup_performance_report.md
28+
M docs_build/dev/reports/test_cleanup_routing_report.md
29+
M docs_build/dev/reports/testing_lane_execution_report.md
30+
M docs_build/dev/reports/validation_cache_report.md
31+
M docs_build/dev/reports/zero_browser_preflight_report.md
32+
M src/dev-runtime/admin/header-nav.local.html
433
M tests/playwright/tools/InputMappingV2Tool.spec.mjs
534
M toolbox/controls/controls.js
635
M toolbox/controls/index.html
7-
?? docs_build/dev/reports/controls-readable-input-columns-report.md
36+
?? docs_build/dev/reports/controls-game-and-user-input-foundation-report.md
37+
38+
# git ls-files --others --exclude-standard
39+
docs_build/dev/reports/controls-game-and-user-input-foundation-report.md
40+
41+
# git diff --stat
42+
account/user-controls-page.js | 76 +++++++--
43+
account/user-controls.html | 19 +++
44+
.../dev/reports/coverage_changed_js_guardrail.txt | 53 +-----
45+
docs_build/dev/reports/dependency_gating_report.md | 2 +-
46+
.../reports/dependency_hydration_reuse_report.md | 12 +-
47+
.../dev/reports/execution_graph_reuse_report.md | 16 +-
48+
.../dev/reports/failure_fingerprint_report.md | 2 +-
49+
.../reports/filesystem_scan_reduction_report.md | 2 +-
50+
.../dev/reports/incremental_validation_report.md | 14 +-
51+
docs_build/dev/reports/lane_compilation_report.md | 2 +-
52+
.../dev/reports/lane_deduplication_report.md | 2 +-
53+
.../dev/reports/lane_input_validation_report.md | 2 +-
54+
.../reports/lane_runtime_optimization_report.md | 14 +-
55+
docs_build/dev/reports/lane_snapshot_report.md | 12 +-
56+
docs_build/dev/reports/lane_warm_start_report.md | 12 +-
57+
.../dev/reports/monolith_trigger_removal_report.md | 2 +-
58+
.../dev/reports/persistent_lane_manifest_report.md | 12 +-
59+
.../playwright_discovery_ownership_report.md | 2 +-
60+
.../reports/playwright_discovery_scope_report.md | 2 +-
61+
.../dev/reports/playwright_structure_audit.md | 2 +-
62+
.../dev/reports/playwright_v8_coverage_report.txt | 20 +--
63+
docs_build/dev/reports/retry_suppression_report.md | 2 +-
64+
docs_build/dev/reports/slow_path_pruning_report.md | 16 +-
65+
docs_build/dev/reports/static_validation_report.md | 10 +-
66+
.../dev/reports/targeted_file_manifest_report.md | 8 +-
67+
.../dev/reports/test_cleanup_performance_report.md | 22 +--
68+
.../dev/reports/test_cleanup_routing_report.md | 2 +-
69+
.../dev/reports/testing_lane_execution_report.md | 52 +++---
70+
docs_build/dev/reports/validation_cache_report.md | 30 ++--
71+
.../dev/reports/zero_browser_preflight_report.md | 10 +-
72+
src/dev-runtime/admin/header-nav.local.html | 1 +
73+
tests/playwright/tools/InputMappingV2Tool.spec.mjs | 129 +++++++++++----
74+
toolbox/controls/controls.js | 178 ++++++++++++++------
75+
toolbox/controls/index.html | 183 +++++++++++++++------
76+
34 files changed, 579 insertions(+), 344 deletions(-)

0 commit comments

Comments
 (0)