-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement glfw joystick API #5175
Conversation
This is functional but is it ok to refresh the joystick status the calls to glfwGetJoystickAxes and glfwGetJoystickButtons? Wasn't sure if the joystick state should be read on each frame instead. Also do I need to free the memory allocated for the axes and buttons arrays (from |
I'm not sure about the refreshing, but looks like it might be fast enough. I guess if you don't see it show up in profiling, it's probably fine. Or was the concern something other than speed? For the allocations, |
src/library_glfw.js
Outdated
@@ -57,6 +56,9 @@ var LibraryGLFW = { | |||
}; | |||
this.buttons = 0; | |||
this.keys = new Array(); | |||
this.joyLast = 0; // GLFW_JOYSTICK_1 | |||
this.joys = {}; | |||
this.index2joy = {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you document here what is the purpose of these three variables. The name index2joy
is a bit ad hoc, it would be good to have some details here.
src/library_glfw.js
Outdated
@@ -382,6 +385,38 @@ var LibraryGLFW = { | |||
#endif | |||
}, | |||
|
|||
onGamepadConnected: function(event) { | |||
if (!GLFW.active.joystickFunc) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if GLFW should be tracking gamepads already before the user code has registered a joystick callback function. For example, it can take a while until the user code runs, and a gamepad connected event may have fired already before. Or perhaps the tightest way would be to enumerate all already known joysticks when a callback is registered or GLW initialized to initially populate the already known joysticks. I'm thinking about the scenario where user is clicking on gamepad buttons way before the page has fully loaded up.
src/library_glfw.js
Outdated
|
||
var joy = GLFW.active.index2joy[event.gamepad.index]; | ||
|
||
Module['dynCall_vii'](GLFW.active.joystickFunc, joy, 0x00040002); // GLFW_DISCONNECTED |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if joy
is undefined
here? I suppose the callback should not be called then if such an odd situation arises?
src/library_glfw.js
Outdated
|
||
Module['dynCall_vii'](GLFW.active.joystickFunc, joy, 0x00040002); // GLFW_DISCONNECTED | ||
|
||
// TODO: free memory .axes, .buttons |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can do _free(GLFW.active.joys[joy].id);
and same for buttons
and axes
. (ALLOC_NORMAL
is equivalent to a _malloc()
)
src/library_glfw.js
Outdated
}, | ||
|
||
onGamepadDisconnected: function(event) { | ||
if (!GLFW.active.joystickFunc) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here also if user code has already detached a user callback function, the code below should still run to deinitialize the data for that joystick?
src/library_glfw.js
Outdated
var j = GLFW.active.joys[joy]; | ||
if (!j) return; | ||
|
||
var gamepad = navigator.getGamepads()[j.index]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading the documentation at http://www.glfw.org/docs/latest/group__input.html#gace54cd930dcd502e118fe4021384ce1b and http://www.glfw.org/docs/latest/input_guide.html#joystick_button, it looks like a suitable usage from user code might be to do a
for(int i = 0; i < numJoysticks; ++i) {
int count;
const unsigned char* axes = glfwGetJoystickButtons(GLFW_JOYSTICK_1+i, &count);
for(int j = 0; j < count; ++j) {
// process axes[j];
}
}
In that case, there is a possible subtle bug due to an unspecced feature of the Gamepad API regarding navigator.getGamepads()
, see w3c/gamepad#22 and w3c/gamepad#8. The issue is that navigator.getGamepads()
polls new state for all gamepads, but the above line only reads data from one of them. So if there are e.g. 3 joysticks, the above loop would poll the state of each of the three joysticks three times, and process only one of them at each loop iteration. This might potentially miss out button presses, but whether that happens in practice is somewhat theoretical. Still good to keep in mind though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/library_glfw.js
Outdated
}, | ||
|
||
glfwGetJoystickName: function(joy) { | ||
return GLFW.active.joys[joy].id; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If joystick is disconnected right before this function is called, GLFW.active.joys[joy]
could be deleted above and be undefined, making this function throw an exception on undefined.id
access; so check for if (GLFW.active.joys[joy])
here before proceeding?
@@ -1076,6 +1076,53 @@ def test_sdl_joystick_2(self): | |||
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_joystick.c'), '-O2', '--minify', '0', '-o', 'page.html', '--pre-js', 'pre.js', '-lSDL', '-lGL']).communicate() | |||
self.run_browser('page.html', '', '/report_result?2') | |||
|
|||
def test_glfw_joystick(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great to have a mock input injecting test! It would be nice to add the same test to the interactive
suite as well, but without the API mock implementation part, so that could be tested with a live gamepad.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a simple interactive test (run with python tests/runner.py interactive.test_glfw_joystick
), no graphics it only logs text of the gamepad state changes but useful for testing (already found & fixed the axes memory allocation error thanks to it)
As opposed to the glfw joysticks, which of course are bridged to the same HTML5 Gamepad API on the web build, but, there may be issues so adding USE_EM_GAMEPAD to switch back (off by default) for testing. This was previously removed in #80 Use glfw joystick everywhere, remove emscripten gamepad due to including an early version of emscripten-core/emscripten#5175 [WIP] Implement glfw joystick API
This reverts commit afc7831.
Incorporated most (but not all) of the review feedback. Next major change needed is to poll the gamepad state once per frame like in #4292. This should also solve the issues I'm seeing on Chrome where gamepadconnected is fired on disconnect. Wondering if it is worth preserving this API nuance of glfw: // HTML5 Gamepad API reuses indexes
// glfw joystick API spreads them out
// http://www.glfw.org/docs/latest/input_guide.html#joystick
// "Once a joystick is detected, it keeps its assigned index until it is disconnected,
// so as joysticks are connected and disconnected, they will become spread out." Also I added _free() but not to |
GLFW_JOYSTICK_1 is 0, so HTML5 Gamepad index 0 maps to GLFW_JOYSTICK_1. Note: The API subtlety of spreading out glfw joystick IDs is lost.
…-coreGH-4292 Joystick connected/disconnected callbacks are also now called in refreshJoysticks(). The gamepadconnected/disconnected events are only used as a hint to refresh the joysticks, whose getGamepads() state is compared to the previous state to determine the new state.
Changed to sample once per frame, tracking joystick connect/disconnect on refresh, and fixed the axes memory freeing. I think that's everything, anything else? |
This looks good to me, but let's wait a bit more for @juj. |
Looks like @juj has no immediate concerns here, so let's merge this, any further stuff can be done in followup work. |
Adds support for glfw3's joystick functions: http://www.glfw.org/docs/latest/input_guide.html#joystick
Includes a test based on the sdl joystick test:
Also manually tested with a real joystick and a more complex real-world application which had native glfw joystick support (satoshinm/NetCraft#80)
python tests/runner.py browser.test_glfw_joystick
python tests/runner.py interactive.test_glfw_joystick