2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
22 #include "../../SDL_internal.h"
24 #ifdef SDL_JOYSTICK_EMSCRIPTEN
26 #include <stdio.h> /* For the definition of NULL */
27 #include "SDL_error.h"
28 #include "SDL_events.h"
30 #if !SDL_EVENTS_DISABLED
31 #include "../../events/SDL_events_c.h"
34 #include "SDL_joystick.h"
35 #include "SDL_hints.h"
36 #include "SDL_assert.h"
37 #include "SDL_timer.h"
39 #include "SDL_sysjoystick_c.h"
40 #include "../SDL_joystick_c.h"
42 static SDL_joylist_item * JoystickByIndex(int index);
44 static SDL_joylist_item *SDL_joylist = NULL;
45 static SDL_joylist_item *SDL_joylist_tail = NULL;
46 static int numjoysticks = 0;
47 static int instance_counter = 0;
50 Emscripten_JoyStickConnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
54 SDL_joylist_item *item;
56 if (JoystickByIndex(gamepadEvent->index) != NULL) {
60 #if !SDL_EVENTS_DISABLED
64 item = (SDL_joylist_item *) SDL_malloc(sizeof (SDL_joylist_item));
70 item->index = gamepadEvent->index;
72 item->name = SDL_strdup(gamepadEvent->id);
73 if ( item->name == NULL ) {
78 item->mapping = SDL_strdup(gamepadEvent->mapping);
79 if ( item->mapping == NULL ) {
85 item->naxes = gamepadEvent->numAxes;
86 item->nbuttons = gamepadEvent->numButtons;
87 item->device_instance = instance_counter++;
89 item->timestamp = gamepadEvent->timestamp;
91 for( i = 0; i < item->naxes; i++) {
92 item->axis[i] = gamepadEvent->axis[i];
95 for( i = 0; i < item->nbuttons; i++) {
96 item->analogButton[i] = gamepadEvent->analogButton[i];
97 item->digitalButton[i] = gamepadEvent->digitalButton[i];
100 if (SDL_joylist_tail == NULL) {
101 SDL_joylist = SDL_joylist_tail = item;
103 SDL_joylist_tail->next = item;
104 SDL_joylist_tail = item;
108 #ifdef DEBUG_JOYSTICK
109 SDL_Log("Number of joysticks is %d", numjoysticks);
111 #if !SDL_EVENTS_DISABLED
112 event.type = SDL_JOYDEVICEADDED;
114 if (SDL_GetEventState(event.type) == SDL_ENABLE) {
115 event.jdevice.which = numjoysticks - 1;
116 if ( (SDL_EventOK == NULL) ||
117 (*SDL_EventOK) (SDL_EventOKParam, &event) ) {
118 SDL_PushEvent(&event);
121 #endif /* !SDL_EVENTS_DISABLED */
123 #ifdef DEBUG_JOYSTICK
124 SDL_Log("Added joystick with index %d", item->index);
131 Emscripten_JoyStickDisconnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
133 SDL_joylist_item *item = SDL_joylist;
134 SDL_joylist_item *prev = NULL;
135 #if !SDL_EVENTS_DISABLED
139 while (item != NULL) {
140 if (item->index == gamepadEvent->index) {
151 if (item->joystick) {
152 item->joystick->hwdata = NULL;
156 prev->next = item->next;
158 SDL_assert(SDL_joylist == item);
159 SDL_joylist = item->next;
161 if (item == SDL_joylist_tail) {
162 SDL_joylist_tail = prev;
165 /* Need to decrement the joystick count before we post the event */
168 #if !SDL_EVENTS_DISABLED
169 event.type = SDL_JOYDEVICEREMOVED;
171 if (SDL_GetEventState(event.type) == SDL_ENABLE) {
172 event.jdevice.which = item->device_instance;
173 if ( (SDL_EventOK == NULL) ||
174 (*SDL_EventOK) (SDL_EventOKParam, &event) ) {
175 SDL_PushEvent(&event);
178 #endif /* !SDL_EVENTS_DISABLED */
180 #ifdef DEBUG_JOYSTICK
181 SDL_Log("Removed joystick with id %d", item->device_instance);
183 SDL_free(item->name);
184 SDL_free(item->mapping);
189 /* Function to scan the system for joysticks.
190 * It should return 0, or -1 on an unrecoverable fatal error.
193 SDL_SYS_JoystickInit(void)
195 int retval, i, numjs;
196 EmscriptenGamepadEvent gamepadState;
199 numjs = emscripten_get_num_gamepads();
201 /* Check if gamepad is supported by browser */
202 if (numjs == EMSCRIPTEN_RESULT_NOT_SUPPORTED) {
203 return SDL_SetError("Gamepads not supported");
206 /* handle already connected gamepads */
208 for(i = 0; i < numjs; i++) {
209 retval = emscripten_get_gamepad_status(i, &gamepadState);
210 if (retval == EMSCRIPTEN_RESULT_SUCCESS) {
211 Emscripten_JoyStickConnected(EMSCRIPTEN_EVENT_GAMEPADCONNECTED,
218 retval = emscripten_set_gamepadconnected_callback(NULL,
220 Emscripten_JoyStickConnected);
222 if(retval != EMSCRIPTEN_RESULT_SUCCESS) {
223 SDL_SYS_JoystickQuit();
224 return SDL_SetError("Could not set gamepad connect callback");
227 retval = emscripten_set_gamepaddisconnected_callback(NULL,
229 Emscripten_JoyStickDisconnected);
230 if(retval != EMSCRIPTEN_RESULT_SUCCESS) {
231 SDL_SYS_JoystickQuit();
232 return SDL_SetError("Could not set gamepad disconnect callback");
238 /* Returns item matching given SDL device index. */
239 static SDL_joylist_item *
240 JoystickByDeviceIndex(int device_index)
242 SDL_joylist_item *item = SDL_joylist;
244 while (0 < device_index) {
252 /* Returns item matching given HTML gamepad index. */
253 static SDL_joylist_item *
254 JoystickByIndex(int index)
256 SDL_joylist_item *item = SDL_joylist;
262 while (item != NULL) {
263 if (item->index == index) {
272 int SDL_SYS_NumJoysticks()
277 void SDL_SYS_JoystickDetect()
281 /* Function to get the device-dependent name of a joystick */
283 SDL_SYS_JoystickNameForDeviceIndex(int device_index)
285 return JoystickByDeviceIndex(device_index)->name;
288 /* Function to perform the mapping from device index to the instance id for this index */
289 SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
291 return JoystickByDeviceIndex(device_index)->device_instance;
294 /* Function to open a joystick for use.
295 The joystick to open is specified by the device index.
296 This should fill the nbuttons and naxes fields of the joystick structure.
297 It returns 0, or -1 if there is an error.
300 SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
302 SDL_joylist_item *item = JoystickByDeviceIndex(device_index);
305 return SDL_SetError("No such device");
308 if (item->joystick != NULL) {
309 return SDL_SetError("Joystick already opened");
312 joystick->instance_id = item->device_instance;
313 joystick->hwdata = (struct joystick_hwdata *) item;
314 item->joystick = joystick;
316 /* HTML5 Gamepad API doesn't say anything about these */
318 joystick->nballs = 0;
320 joystick->nbuttons = item->nbuttons;
321 joystick->naxes = item->naxes;
326 /* Function to determine if this joystick is attached to the system right now */
327 SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
329 return joystick->hwdata != NULL;
332 /* Function to update the state of a joystick - called as a device poll.
333 * This function shouldn't update the joystick structure directly,
334 * but instead should call SDL_PrivateJoystick*() to deliver events
335 * and update joystick device state.
338 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
340 EmscriptenGamepadEvent gamepadState;
341 SDL_joylist_item *item = (SDL_joylist_item *) joystick->hwdata;
342 int i, result, buttonState;
345 result = emscripten_get_gamepad_status(item->index, &gamepadState);
346 if( result == EMSCRIPTEN_RESULT_SUCCESS) {
347 if(gamepadState.timestamp == 0 || gamepadState.timestamp != item->timestamp) {
348 for(i = 0; i < item->nbuttons; i++) {
349 if(item->digitalButton[i] != gamepadState.digitalButton[i]) {
350 buttonState = gamepadState.digitalButton[i]? SDL_PRESSED: SDL_RELEASED;
351 SDL_PrivateJoystickButton(item->joystick, i, buttonState);
354 /* store values to compare them in the next update */
355 item->analogButton[i] = gamepadState.analogButton[i];
356 item->digitalButton[i] = gamepadState.digitalButton[i];
359 for(i = 0; i < item->naxes; i++) {
360 if(item->axis[i] != gamepadState.axis[i]) {
361 // do we need to do conversion?
362 SDL_PrivateJoystickAxis(item->joystick, i,
363 (Sint16) (32767.*gamepadState.axis[i]));
366 /* store to compare in next update */
367 item->axis[i] = gamepadState.axis[i];
370 item->timestamp = gamepadState.timestamp;
376 /* Function to close a joystick after use */
378 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
382 /* Function to perform any system-specific joystick related cleanup */
384 SDL_SYS_JoystickQuit(void)
386 SDL_joylist_item *item = NULL;
387 SDL_joylist_item *next = NULL;
389 for (item = SDL_joylist; item; item = next) {
391 SDL_free(item->mapping);
392 SDL_free(item->name);
396 SDL_joylist = SDL_joylist_tail = NULL;
399 instance_counter = 0;
401 emscripten_set_gamepadconnected_callback(NULL, 0, NULL);
402 emscripten_set_gamepaddisconnected_callback(NULL, 0, NULL);
406 SDL_SYS_JoystickGetDeviceGUID(int device_index)
408 SDL_JoystickGUID guid;
409 /* the GUID is just the first 16 chars of the name for now */
410 const char *name = SDL_SYS_JoystickNameForDeviceIndex(device_index);
412 SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
417 SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
419 SDL_JoystickGUID guid;
420 /* the GUID is just the first 16 chars of the name for now */
421 const char *name = joystick->name;
423 SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
427 #endif /* SDL_JOYSTICK_EMSCRIPTEN */