feat(playground): expand gamepad support with face buttons and Steam Deck fixes

- Map A=Train, B=Clear Examples, X=Randomize, LB=Thumbs Down, RB=Thumbs Up
- Add periodic gamepad polling fallback for environments where
  gamepadconnected event doesn't fire reliably (Steam Deck, some Linux browsers)
- Show "Press any gamepad button to connect" hint on page load
This commit is contained in:
w1n5t0n 2026-03-22 22:40:15 +02:00
parent affec8f604
commit de75a3f0e4
4 changed files with 50 additions and 17 deletions

View file

@ -1365,14 +1365,23 @@ function wireGamepad() {
onJoystickMove(); onJoystickMove();
}, },
onButton: (btn) => { onButton: (btn) => {
if (btn === 'rb') onThumbsUp();
if (btn === 'lb') onThumbsDown(); if (btn === 'lb') onThumbsDown();
if (btn === 'rb') onThumbsUp();
if (btn === 'a') onTrain();
if (btn === 'x') onRandomize();
if (btn === 'b') onClearExamples();
}, },
onConnectionChange: (connected) => { onConnectionChange: (connected) => {
const el = document.getElementById('gamepad-status'); const el = document.getElementById('gamepad-status');
if (el) el.textContent = connected ? 'Gamepad connected' : ''; if (el) el.textContent = connected ? 'Gamepad connected' : 'Press any button to activate gamepad';
}, },
}); });
// Show hint immediately if no gamepad detected yet
if (!gamepad.connected) {
const el = document.getElementById('gamepad-status');
if (el) el.textContent = 'Press any gamepad button to connect';
}
} }
// ---- Keyboard ---- // ---- Keyboard ----

View file

@ -1241,15 +1241,23 @@ function init() {
mappingView.drawOverlay(); mappingView.drawOverlay();
}, },
onButton: (btn) => { onButton: (btn) => {
if (btn === 'rb') onThumbsUp();
if (btn === 'lb') onThumbsDown(); if (btn === 'lb') onThumbsDown();
if (btn === 'rb') onThumbsUp();
if (btn === 'a') onTrain();
if (btn === 'x') onRandomize();
if (btn === 'b') onClearExamples();
}, },
onConnectionChange: (connected) => { onConnectionChange: (connected) => {
const el = document.getElementById('gamepad-status'); const el = document.getElementById('gamepad-status');
if (el) el.textContent = connected ? 'Gamepad connected' : ''; if (el) el.textContent = connected ? 'Gamepad connected' : 'Press any button to activate gamepad';
}, },
}); });
if (!gamepad.connected) {
const el = document.getElementById('gamepad-status');
if (el) el.textContent = 'Press any gamepad button to connect';
}
// Resize // Resize
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
visualizer.resize(); visualizer.resize();

View file

@ -287,15 +287,20 @@ function init() {
drawJoystick(); drawJoystick();
}, },
onButton: (btn) => { onButton: (btn) => {
if (btn === 'rb') onThumbsUp();
if (btn === 'lb') onThumbsDown(); if (btn === 'lb') onThumbsDown();
if (btn === 'rb') onThumbsUp();
}, },
onConnectionChange: (connected) => { onConnectionChange: (connected) => {
const el = document.getElementById('gamepad-status'); const el = document.getElementById('gamepad-status');
if (el) el.textContent = connected ? 'Gamepad connected' : ''; if (el) el.textContent = connected ? 'Gamepad connected' : 'Press any button to activate gamepad';
}, },
}); });
if (!gamepad.connected) {
const el = document.getElementById('gamepad-status');
if (el) el.textContent = 'Press any gamepad button to connect';
}
// Follow mode wiring // Follow mode wiring
wireFollowMode(); wireFollowMode();

View file

@ -37,6 +37,17 @@ export class GamepadInput {
// Check if a gamepad is already connected // Check if a gamepad is already connected
this._findGamepad(); this._findGamepad();
// Periodic fallback: some environments (Steam Deck, some Linux browsers)
// don't fire gamepadconnected reliably. Poll every 500ms until found.
this._probeInterval = setInterval(() => {
if (!this.connected) {
this._findGamepad();
} else {
clearInterval(this._probeInterval);
this._probeInterval = null;
}
}, 500);
} }
_onConnected(e) { _onConnected(e) {
@ -126,18 +137,17 @@ export class GamepadInput {
this.onMove(mappedX, mappedY); this.onMove(mappedX, mappedY);
} }
// Buttons: LB=4, RB=5 // Buttons: A=0, B=1, X=2, Y=3, LB=4, RB=5
if (this.onButton) { if (this.onButton) {
const lbPressed = !!gp.buttons[4]?.pressed; const watched = [0, 1, 2, 3, 4, 5];
const rbPressed = !!gp.buttons[5]?.pressed; const names = ['a', 'b', 'x', 'y', 'lb', 'rb'];
const lbPrev = !!this._buttonsPrev[4]; for (let i = 0; i < watched.length; i++) {
const rbPrev = !!this._buttonsPrev[5]; const idx = watched[i];
const pressed = !!gp.buttons[idx]?.pressed;
if (lbPressed && !lbPrev) this.onButton('lb'); const prev = !!this._buttonsPrev[idx];
if (rbPressed && !rbPrev) this.onButton('rb'); if (pressed && !prev) this.onButton(names[i]);
this._buttonsPrev[idx] = pressed;
this._buttonsPrev[4] = lbPressed; }
this._buttonsPrev[5] = rbPressed;
} }
return moved; return moved;
@ -153,5 +163,6 @@ export class GamepadInput {
destroy() { destroy() {
window.removeEventListener('gamepadconnected', this._onConnected); window.removeEventListener('gamepadconnected', this._onConnected);
window.removeEventListener('gamepaddisconnected', this._onDisconnected); window.removeEventListener('gamepaddisconnected', this._onDisconnected);
if (this._probeInterval) clearInterval(this._probeInterval);
} }
} }