Skip to content

Commit f217734

Browse files
committed
Improve gamepad input handling and logging
Enhanced gamepad input polling and validation in both C++ and JS, adding detailed comments and more robust checks for connection and timestamp validity. Updated log messages for clarity and consistency, and improved event handling for gamepad connect/disconnect scenarios. These changes help with debugging and ensure better synchronization between native and JS gamepad logic.
1 parent 0c374c5 commit f217734

File tree

5 files changed

+76
-46
lines changed

5 files changed

+76
-46
lines changed

wasm/gamepad.cpp

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,17 @@ enum GamepadAxis {
5050
RightY = 3,
5151
};
5252

53-
// Function to create a mask for active gamepads
53+
// Function to get the active gamepad mask
5454
static short GetActiveGamepadMask(int numGamepads) {
5555
short result = 0;
56-
56+
57+
// Iterate through the number of gamepads
5758
for (int i = 0; i < numGamepads; ++i) {
59+
// Set the corresponding bits in the mask
5860
result |= (1 << i);
5961
}
60-
62+
63+
// Return the active gamepad mask
6164
return result;
6265
}
6366

@@ -130,7 +133,8 @@ static short GetButtonFlags(const EmscriptenGamepadEvent& gamepad) {
130133
}
131134

132135
short result = 0;
133-
136+
137+
// Iterate through the buttons and set the corresponding flags
134138
for (int i = 0; i < gamepad.numButtons && i < buttonMasksSize; ++i) {
135139
if (gamepad.digitalButton[i] == EM_TRUE) {
136140
result |= buttonMasks[i];
@@ -150,14 +154,16 @@ void MoonlightInstance::HandleGamepadInputState(bool rumbleFeedback, bool mouseE
150154

151155
// Function to poll gamepad input
152156
void MoonlightInstance::PollGamepads() {
157+
// Check if sampling gamepad data is successful
153158
if (emscripten_sample_gamepad_data() != EMSCRIPTEN_RESULT_SUCCESS) {
154-
std::cerr << "Sample gamepad data failed!\n";
159+
ClLogMessage("Failed to sample gamepad data!\n");
155160
return;
156161
}
157162

158163
const auto numGamepads = emscripten_get_num_gamepads();
164+
// Check if getting the number of gamepads is supported
159165
if (numGamepads == EMSCRIPTEN_RESULT_NOT_SUPPORTED) {
160-
std::cerr << "Get num gamepads failed!\n";
166+
ClLogMessage("Failed to get number of gamepads!\n");
161167
return;
162168
}
163169

@@ -171,36 +177,39 @@ void MoonlightInstance::PollGamepads() {
171177
for (int gamepadID = 0; gamepadID < numGamepads; ++gamepadID) {
172178
emscripten_sample_gamepad_data();
173179
EmscriptenGamepadEvent gamepad;
174-
// See logic in getConnectedGamepadMask() (utils.js)
175-
// These must stay in sync!
180+
// See logic in `utils.js`, these must stay in sync!
176181

177182
const auto result = emscripten_get_gamepad_status(gamepadID, &gamepad);
178-
if (result != EMSCRIPTEN_RESULT_SUCCESS || !gamepad.connected) {
179-
// Not connected
183+
// Check if getting the gamepad status is successful
184+
if (result != EMSCRIPTEN_RESULT_SUCCESS) {
185+
// No gamepad status, skip it
180186
continue;
181187
}
182188

189+
// Check if the gamepad is connected
190+
if (!gamepad.connected) {
191+
// Not connected, skip it
192+
ClLogMessage("Gamepad %d is not connected, skipping!\n", gamepadID);
193+
continue;
194+
}
195+
196+
// Check if the gamepad has a non-zero timestamp
183197
if (gamepad.timestamp == 0) {
184198
// On some platforms, Tizen returns "connected" gamepads that really
185199
// aren't, so timestamp stays at zero. To work around this, we'll only
186200
// count gamepads that have a non-zero timestamp in our controller index.
187-
continue;
201+
ClLogMessage("Gamepad %d has zero timestamp, skipping!\n", gamepadID);
202+
continue; // Not valid, skip it
188203
}
189204

190-
// Process input for active gamepad
205+
// Get the button flags, trigger, and analog values from the gamepad
191206
const auto buttonFlags = GetButtonFlags(gamepad);
192-
const auto leftTrigger = gamepad.analogButton[GamepadButton::LeftTrigger]
193-
* std::numeric_limits<unsigned char>::max();
194-
const auto rightTrigger = gamepad.analogButton[GamepadButton::RightTrigger]
195-
* std::numeric_limits<unsigned char>::max();
196-
const auto leftStickX = gamepad.axis[GamepadAxis::LeftX]
197-
* std::numeric_limits<short>::max();
198-
const auto leftStickY = -gamepad.axis[GamepadAxis::LeftY]
199-
* std::numeric_limits<short>::max();
200-
const auto rightStickX = gamepad.axis[GamepadAxis::RightX]
201-
* std::numeric_limits<short>::max();
202-
const auto rightStickY = -gamepad.axis[GamepadAxis::RightY]
203-
* std::numeric_limits<short>::max();
207+
const auto leftTrigger = gamepad.analogButton[GamepadButton::LeftTrigger] * std::numeric_limits<unsigned char>::max();
208+
const auto rightTrigger = gamepad.analogButton[GamepadButton::RightTrigger] * std::numeric_limits<unsigned char>::max();
209+
const auto leftStickX = gamepad.axis[GamepadAxis::LeftX] * std::numeric_limits<short>::max();
210+
const auto leftStickY = -gamepad.axis[GamepadAxis::LeftY] * std::numeric_limits<short>::max();
211+
const auto rightStickX = gamepad.axis[GamepadAxis::RightX] * std::numeric_limits<short>::max();
212+
const auto rightStickY = -gamepad.axis[GamepadAxis::RightY] * std::numeric_limits<short>::max();
204213

205214
// Check if the current button flags match the defined button combination on the gamepad
206215
if (buttonFlags == STOP_STREAM_BUTTONS) {
@@ -298,9 +307,7 @@ void MoonlightInstance::PollGamepads() {
298307
}
299308
} else {
300309
// If mouse emulation is inactive, then send gamepad input to the desired handler (acts as a gamepad)
301-
LiSendMultiControllerEvent(
302-
gamepadID, activeGamepadMask, buttonFlags, leftTrigger,
303-
rightTrigger, leftStickX, leftStickY, rightStickX, rightStickY);
310+
LiSendMultiControllerEvent(gamepadID, activeGamepadMask, buttonFlags, leftTrigger, rightTrigger, leftStickX, leftStickY, rightStickX, rightStickY);
304311
}
305312
}
306313
}

wasm/main.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,10 @@ void* MoonlightInstance::InputThreadFunc(void* context) {
124124
MoonlightInstance* me = (MoonlightInstance*)context;
125125

126126
while (me->m_Running) {
127+
// Poll gamepad input
127128
me->PollGamepads();
129+
// Report mouse movement
128130
me->ReportMouseMovement();
129-
130131
// Poll every 5 ms
131132
usleep(5 * 1000);
132133
}

wasm/platform/gamepad.js

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ const Controller = (function() {
1111

1212
class Gamepad {
1313
constructor(gamepad) {
14+
// Store initial button and axis states
1415
this.buttons = gamepad.buttons.map((button) => new Button(button));
15-
this.axes = gamepad.axes.slice(); // Store initial axis values
16+
this.axes = gamepad.axes.slice();
1617
}
1718

1819
analyzeButtonsAndAxes(newButtons, newAxes) {
@@ -23,6 +24,7 @@ const Controller = (function() {
2324

2425
const changes = [];
2526

27+
// Check for button state changes
2628
for (let i = 0; i < newButtons.length; ++i) {
2729
if (this.buttons[i].pressed !== newButtons[i].pressed) {
2830
changes.push({
@@ -33,6 +35,7 @@ const Controller = (function() {
3335
}
3436
}
3537

38+
// Check for axis value changes
3639
for (let i = 0; i < newAxes.length; i++) {
3740
if (this.axes[i] !== newAxes[i]) {
3841
changes.push({
@@ -43,62 +46,74 @@ const Controller = (function() {
4346
}
4447
}
4548

49+
// If there are any changes detected, dispatch an event
4650
if (changes.length > 0) {
51+
// Dispatch a custom event with the detected changes
4752
window.dispatchEvent(new CustomEvent('gamepadinputchanged', {
4853
detail: { changes },
4954
})
5055
);
5156
}
5257

58+
// Update stored button and axis states
5359
this.buttons = newButtons.map((button) => new Button(button));
54-
this.axes = newAxes.slice(); // Update stored axis values
60+
this.axes = newAxes.slice();
5561
}
5662
}
5763

5864
function gamepadConnected(gamepad) {
65+
// Add the connected gamepad to the registry
5966
gamepads[gamepad.index] = new Gamepad(gamepad);
6067
}
6168

6269
function gamepadDisconnected(gamepad) {
70+
// Remove the disconnected gamepad from the registry
6371
delete gamepads[gamepad.index];
6472
}
6573

6674
function analyzeGamepad(gamepad) {
6775
const index = gamepad.index;
6876
const pGamepad = gamepads[index];
6977

78+
// If the gamepad is registered, analyze its buttons and axes
7079
if (pGamepad) {
7180
pGamepad.analyzeButtonsAndAxes(gamepad.buttons, gamepad.axes);
7281
}
7382
}
7483

7584
function pollGamepads() {
76-
const gamepads = navigator.getGamepads
77-
? navigator.getGamepads()
78-
: navigator.webkitGetGamepads
79-
? navigator.webkitGetGamepads
80-
: [];
85+
// Retrieve the list of connected gamepads
86+
const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
87+
// Analyze each connected gamepad
8188
for (const gamepad of gamepads) {
89+
// Check if the gamepad is valid
8290
if (gamepad) {
91+
// Analyze only valid gamepads
8392
analyzeGamepad(gamepad);
8493
}
8594
}
8695
}
8796

8897
function startWatching() {
98+
// Start polling only if not already started
8999
if (!pollingInterval) {
100+
console.log('%c[gamepad.js, startWatching]', 'color: gray;', 'Starting gamepad polling...');
101+
// Listen for gamepad connection and disconnection events
90102
window.addEventListener('gamepadconnected', function(e) {
91103
gamepadConnected(e.gamepad);
92104
});
93105
window.addEventListener('gamepaddisconnected', function(e) {
94106
gamepadDisconnected(e.gamepad);
95107
});
108+
// Start polling at regular intervals (every 5 ms)
96109
pollingInterval = setInterval(pollGamepads, 5);
97110
}
98111
}
99112

100113
function stopWatching() {
114+
// Stop polling if it is currently active
101115
if (pollingInterval) {
116+
console.log('%c[gamepad.js, stopWatching]', 'color: gray;', 'Stopping gamepad polling...');
102117
clearInterval(pollingInterval);
103118
pollingInterval = null;
104119
}

wasm/platform/index.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3315,8 +3315,8 @@ window.addEventListener('gamepadconnected', function(e) {
33153315
const connectedGamepad = e.gamepad;
33163316
const gamepadIndex = connectedGamepad.index;
33173317
const rumbleFeedbackSwitch = document.getElementById('rumbleFeedbackSwitch');
3318-
console.log('%c[index.js, gamepadconnected]', 'color: green;', 'Gamepad connected:\n' + JSON.stringify(connectedGamepad), connectedGamepad);
3319-
snackbarLog('Gamepad ' + gamepadIndex + ' has been connected.');
3318+
console.log('%c[index.js, gamepadconnected]', 'color: green;', 'Connected gamepad:\n' + JSON.stringify(connectedGamepad), connectedGamepad);
3319+
snackbarLog('Gamepad connected');
33203320
// Check if the rumble feedback switch is checked
33213321
if (rumbleFeedbackSwitch.checked) {
33223322
// Check if the connected gamepad has a vibrationActuator associated with it
@@ -3329,7 +3329,7 @@ window.addEventListener('gamepadconnected', function(e) {
33293329
strongMagnitude: 0.5,
33303330
});
33313331
} else {
3332-
console.warn('%c[index.js, gamepadconnected]', 'color: green;', 'Warning: Connected gamepad ' + gamepadIndex + ' does not support the rumble feature!');
3332+
console.info('%c[index.js, gamepadconnected]', 'color: green;', 'Connected gamepad ' + gamepadIndex + ' does not support rumble.');
33333333
}
33343334
}
33353335
});
@@ -3338,7 +3338,7 @@ window.addEventListener('gamepadconnected', function(e) {
33383338
window.addEventListener('gamepaddisconnected', function(e) {
33393339
const disconnectedGamepad = e.gamepad;
33403340
const gamepadIndex = disconnectedGamepad.index;
3341-
console.log('%c[index.js, gamepaddisconnected]', 'color: green;', 'Gamepad disconnected:\n' + JSON.stringify(disconnectedGamepad), disconnectedGamepad);
3342-
snackbarLog('Gamepad ' + gamepadIndex + ' has been disconnected.');
3343-
console.warn('%c[index.js, gamepaddisconnected]', 'color: green;', 'Warning: Lost connection to gamepad ' + gamepadIndex + '. Please reconnect your gamepad!');
3341+
console.log('%c[index.js, gamepaddisconnected]', 'color: green;', 'Disconnected gamepad:\n' + JSON.stringify(disconnectedGamepad), disconnectedGamepad);
3342+
snackbarLog('Gamepad disconnected');
3343+
console.info('%c[index.js, gamepaddisconnected]', 'color: green;', 'Lost connection to gamepad ' + gamepadIndex + '. Please reconnect your gamepad!');
33443344
});

wasm/static/js/utils.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,29 +45,36 @@ function getConnectedGamepadMask() {
4545
var mask = 0;
4646
var gamepads = navigator.getGamepads ? navigator.getGamepads() : [];
4747

48+
// Iterate through the gamepads and build the bitmask
4849
for (var i = 0; i < gamepads.length; i++) {
4950
var gamepad = gamepads[i];
51+
// Check if gamepad is valid
5052
if (gamepad) {
51-
// See logic in gamepad.cpp
52-
// These must stay in sync!
53+
// See logic in `gamepad.cpp`, these must stay in sync!
5354

55+
// Check if the gamepad is connected
5456
if (!gamepad.connected) {
55-
// Not connected
57+
// Not connected, skip it
58+
console.warn('%c[utils.js, getConnectedGamepadMask]', 'color: gray;', 'Warning: Gamepad at index ' + i + ' is not connected, skipping it!');
5659
continue;
5760
}
5861

62+
// Check if the gamepad has a non-zero timestamp
5963
if (gamepad.timestamp == 0) {
6064
// On some platforms, Tizen returns "connected" gamepads that really
6165
// aren't, so timestamp stays at zero. To work around this, we'll only
6266
// count gamepads that have a non-zero timestamp in our controller index.
63-
continue;
67+
console.warn('%c[utils.js, getConnectedGamepadMask]', 'color: gray;', 'Warning: Gamepad at index ' + i + ' has zero timestamp, skipping it!');
68+
continue; // Not valid, skip it
6469
}
6570

71+
// Set the corresponding bit in the mask
6672
mask |= 1 << count++;
6773
}
6874
}
6975

70-
console.log('%c[utils.js, getConnectedGamepadMask]', 'color: gray;', 'Detected: ' + count + ' gamepads.');
76+
console.log('%c[utils.js, getConnectedGamepadMask]', 'color: gray;', 'Detected: ' + count + ' ' + (count === 1 ? 'gamepad' : 'gamepads'));
77+
// Return the constructed bitmask
7178
return mask;
7279
}
7380

0 commit comments

Comments
 (0)