-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpreload.js
More file actions
106 lines (88 loc) · 3.29 KB
/
Copy pathpreload.js
File metadata and controls
106 lines (88 loc) · 3.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
const { ipcRenderer } = require('electron');
// Gamepad polling and input state
const DEADZONE = 0.45;
const INITIAL_REPEAT_DELAY = 350; // ms before repeat starts
const REPEAT_INTERVAL = 120; // ms between repeated inputs
// Button index mappings (Standard Gamepad specification)
const BUTTON_MAP = {
0: 'Return', // A / Cross -> Select / OK
1: 'Escape', // B / Circle -> Back
2: 'Space', // X / Square -> Play/Pause
3: '/', // Y / Triangle -> Search
4: 'j', // LB / L1 -> Rewind
5: 'l', // RB / R1 -> Fast forward
8: 'Escape', // Select / View / Share -> Back
9: 'Return' // Start / Menu -> Enter
};
// State trackers across frames
const buttonStates = new Map();
const directionalStates = {
Up: { active: false, nextTime: 0 },
Down: { active: false, nextTime: 0 },
Left: { active: false, nextTime: 0 },
Right: { active: false, nextTime: 0 }
};
function triggerKey(key) {
ipcRenderer.send('controller-key', key);
}
function handleDirection(dir, isPressed, now) {
const state = directionalStates[dir];
if (isPressed) {
if (!state.active) {
// First press
state.active = true;
state.nextTime = now + INITIAL_REPEAT_DELAY;
triggerKey(dir);
} else if (now >= state.nextTime) {
// Repeat while held
state.nextTime = now + REPEAT_INTERVAL;
triggerKey(dir);
}
} else {
state.active = false;
state.nextTime = 0;
}
}
function pollGamepads() {
const gamepads = navigator.getGamepads ? navigator.getGamepads() : [];
const now = Date.now();
for (const gp of gamepads) {
if (!gp) continue;
// 1. Directional controls (combine D-pad buttons and Left Analog Stick)
const stickX = gp.axes && gp.axes.length > 0 ? gp.axes[0] : 0;
const stickY = gp.axes && gp.axes.length > 1 ? gp.axes[1] : 0;
const dpadUp = gp.buttons[12] && gp.buttons[12].pressed;
const dpadDown = gp.buttons[13] && gp.buttons[13].pressed;
const dpadLeft = gp.buttons[14] && gp.buttons[14].pressed;
const dpadRight = gp.buttons[15] && gp.buttons[15].pressed;
const isUp = dpadUp || stickY < -DEADZONE;
const isDown = dpadDown || stickY > DEADZONE;
const isLeft = dpadLeft || stickX < -DEADZONE;
const isRight = dpadRight || stickX > DEADZONE;
handleDirection('Up', isUp, now);
handleDirection('Down', isDown, now);
handleDirection('Left', isLeft, now);
handleDirection('Right', isRight, now);
// 2. Action buttons (single-press triggers)
for (const [btnIndexStr, keyCode] of Object.entries(BUTTON_MAP)) {
const btnIndex = parseInt(btnIndexStr, 10);
const btn = gp.buttons[btnIndex];
const isPressed = btn ? btn.pressed || btn.value > 0.5 : false;
const wasPressed = buttonStates.get(btnIndex) || false;
if (isPressed && !wasPressed) {
triggerKey(keyCode);
}
buttonStates.set(btnIndex, isPressed);
}
// Only process the first active gamepad
break;
}
}
window.addEventListener('gamepadconnected', (e) => {
console.log(`Gamepad connected at index ${e.gamepad.index}: ${e.gamepad.id}. Controller navigation enabled.`);
});
window.addEventListener('gamepaddisconnected', (e) => {
console.log(`Gamepad disconnected at index ${e.gamepad.index}: ${e.gamepad.id}`);
});
// Run gamepad polling loop continuously
setInterval(pollGamepads, 16);