src/joystick/emscripten/SDL_sysjoystick.c
author Charlie Birks <charlie@daftgames.net>
Tue, 29 Jan 2019 12:19:00 +0000
changeset 12575 62f907f026bb
parent 12503 806492103856
permissions -rw-r--r--
Emscripten: call emscripten_sample_gamepad_data
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
     4 
     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.
     8 
     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:
    12 
    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.
    20 */
    21 
    22 #include "../../SDL_internal.h"
    23 
    24 #ifdef SDL_JOYSTICK_EMSCRIPTEN
    25 
    26 #include <stdio.h>              /* For the definition of NULL */
    27 #include "SDL_error.h"
    28 #include "SDL_events.h"
    29 
    30 #include "SDL_joystick.h"
    31 #include "SDL_assert.h"
    32 #include "SDL_timer.h"
    33 #include "SDL_log.h"
    34 #include "SDL_sysjoystick_c.h"
    35 #include "../SDL_joystick_c.h"
    36 
    37 static SDL_joylist_item * JoystickByIndex(int index);
    38 
    39 static SDL_joylist_item *SDL_joylist = NULL;
    40 static SDL_joylist_item *SDL_joylist_tail = NULL;
    41 static int numjoysticks = 0;
    42 static int instance_counter = 0;
    43 
    44 static EM_BOOL
    45 Emscripten_JoyStickConnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
    46 {
    47     int i;
    48 
    49     SDL_joylist_item *item;
    50 
    51     if (JoystickByIndex(gamepadEvent->index) != NULL) {
    52       return 1;
    53     }
    54 
    55     item = (SDL_joylist_item *) SDL_malloc(sizeof (SDL_joylist_item));
    56     if (item == NULL) {
    57         return 1;
    58     }
    59 
    60     SDL_zerop(item);
    61     item->index = gamepadEvent->index;
    62 
    63     item->name = SDL_strdup(gamepadEvent->id);
    64     if ( item->name == NULL ) {
    65         SDL_free(item);
    66         return 1;
    67     }
    68 
    69     item->mapping = SDL_strdup(gamepadEvent->mapping);
    70     if ( item->mapping == NULL ) {
    71         SDL_free(item->name);
    72         SDL_free(item);
    73         return 1;
    74     }
    75 
    76     item->naxes = gamepadEvent->numAxes;
    77     item->nbuttons = gamepadEvent->numButtons;
    78     item->device_instance = instance_counter++;
    79 
    80     item->timestamp = gamepadEvent->timestamp;
    81 
    82     for( i = 0; i < item->naxes; i++) {
    83         item->axis[i] = gamepadEvent->axis[i];
    84     }
    85 
    86     for( i = 0; i < item->nbuttons; i++) {
    87         item->analogButton[i] = gamepadEvent->analogButton[i];
    88         item->digitalButton[i] = gamepadEvent->digitalButton[i];
    89     }
    90 
    91     if (SDL_joylist_tail == NULL) {
    92         SDL_joylist = SDL_joylist_tail = item;
    93     } else {
    94         SDL_joylist_tail->next = item;
    95         SDL_joylist_tail = item;
    96     }
    97 
    98     ++numjoysticks;
    99 
   100     SDL_PrivateJoystickAdded(item->device_instance);
   101 
   102 #ifdef DEBUG_JOYSTICK
   103     SDL_Log("Number of joysticks is %d", numjoysticks);
   104 #endif
   105 
   106 #ifdef DEBUG_JOYSTICK
   107     SDL_Log("Added joystick with index %d", item->index);
   108 #endif
   109 
   110     return 1;
   111 }
   112 
   113 static EM_BOOL
   114 Emscripten_JoyStickDisconnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
   115 {
   116     SDL_joylist_item *item = SDL_joylist;
   117     SDL_joylist_item *prev = NULL;
   118 
   119     while (item != NULL) {
   120         if (item->index == gamepadEvent->index) {
   121             break;
   122         }
   123         prev = item;
   124         item = item->next;
   125     }
   126 
   127     if (item == NULL) {
   128         return 1;
   129     }
   130 
   131     if (item->joystick) {
   132         item->joystick->hwdata = NULL;
   133     }
   134 
   135     if (prev != NULL) {
   136         prev->next = item->next;
   137     } else {
   138         SDL_assert(SDL_joylist == item);
   139         SDL_joylist = item->next;
   140     }
   141     if (item == SDL_joylist_tail) {
   142         SDL_joylist_tail = prev;
   143     }
   144 
   145     /* Need to decrement the joystick count before we post the event */
   146     --numjoysticks;
   147 
   148     SDL_PrivateJoystickRemoved(item->device_instance);
   149 
   150 #ifdef DEBUG_JOYSTICK
   151     SDL_Log("Removed joystick with id %d", item->device_instance);
   152 #endif
   153     SDL_free(item->name);
   154     SDL_free(item->mapping);
   155     SDL_free(item);
   156     return 1;
   157 }
   158 
   159 /* Function to perform any system-specific joystick related cleanup */
   160 static void
   161 EMSCRIPTEN_JoystickQuit(void)
   162 {
   163     SDL_joylist_item *item = NULL;
   164     SDL_joylist_item *next = NULL;
   165 
   166     for (item = SDL_joylist; item; item = next) {
   167         next = item->next;
   168         SDL_free(item->mapping);
   169         SDL_free(item->name);
   170         SDL_free(item);
   171     }
   172 
   173     SDL_joylist = SDL_joylist_tail = NULL;
   174 
   175     numjoysticks = 0;
   176     instance_counter = 0;
   177 
   178     emscripten_set_gamepadconnected_callback(NULL, 0, NULL);
   179     emscripten_set_gamepaddisconnected_callback(NULL, 0, NULL);
   180 }
   181 
   182 /* Function to scan the system for joysticks.
   183  * It should return 0, or -1 on an unrecoverable fatal error.
   184  */
   185 static int
   186 EMSCRIPTEN_JoystickInit(void)
   187 {
   188     int retval, i, numjs;
   189     EmscriptenGamepadEvent gamepadState;
   190 
   191     numjoysticks = 0;
   192 
   193     retval = emscripten_sample_gamepad_data();
   194 
   195     /* Check if gamepad is supported by browser */
   196     if (retval == EMSCRIPTEN_RESULT_NOT_SUPPORTED) {
   197         return SDL_SetError("Gamepads not supported");
   198     }
   199 
   200     numjs = emscripten_get_num_gamepads();
   201 
   202     /* handle already connected gamepads */
   203     if (numjs > 0) {
   204         for(i = 0; i < numjs; i++) {
   205             retval = emscripten_get_gamepad_status(i, &gamepadState);
   206             if (retval == EMSCRIPTEN_RESULT_SUCCESS) {
   207                 Emscripten_JoyStickConnected(EMSCRIPTEN_EVENT_GAMEPADCONNECTED,
   208                                              &gamepadState,
   209                                              NULL);
   210             }
   211         }
   212     }
   213 
   214     retval = emscripten_set_gamepadconnected_callback(NULL,
   215                                                       0,
   216                                                       Emscripten_JoyStickConnected);
   217 
   218     if(retval != EMSCRIPTEN_RESULT_SUCCESS) {
   219         EMSCRIPTEN_JoystickQuit();
   220         return SDL_SetError("Could not set gamepad connect callback");
   221     }
   222 
   223     retval = emscripten_set_gamepaddisconnected_callback(NULL,
   224                                                          0,
   225                                                          Emscripten_JoyStickDisconnected);
   226     if(retval != EMSCRIPTEN_RESULT_SUCCESS) {
   227         EMSCRIPTEN_JoystickQuit();
   228         return SDL_SetError("Could not set gamepad disconnect callback");
   229     }
   230 
   231     return 0;
   232 }
   233 
   234 /* Returns item matching given SDL device index. */
   235 static SDL_joylist_item *
   236 JoystickByDeviceIndex(int device_index)
   237 {
   238     SDL_joylist_item *item = SDL_joylist;
   239 
   240     while (0 < device_index) {
   241         --device_index;
   242         item = item->next;
   243     }
   244 
   245     return item;
   246 }
   247 
   248 /* Returns item matching given HTML gamepad index. */
   249 static SDL_joylist_item *
   250 JoystickByIndex(int index)
   251 {
   252     SDL_joylist_item *item = SDL_joylist;
   253 
   254     if (index < 0) {
   255         return NULL;
   256     }
   257 
   258     while (item != NULL) {
   259         if (item->index == index) {
   260             break;
   261         }
   262         item = item->next;
   263     }
   264 
   265     return item;
   266 }
   267 
   268 static int
   269 EMSCRIPTEN_JoystickGetCount(void)
   270 {
   271     return numjoysticks;
   272 }
   273 
   274 static void
   275 EMSCRIPTEN_JoystickDetect(void)
   276 {
   277 }
   278 
   279 static const char *
   280 EMSCRIPTEN_JoystickGetDeviceName(int device_index)
   281 {
   282     return JoystickByDeviceIndex(device_index)->name;
   283 }
   284 
   285 static int
   286 EMSCRIPTEN_JoystickGetDevicePlayerIndex(int device_index)
   287 {
   288     return -1;
   289 }
   290 
   291 static SDL_JoystickID
   292 EMSCRIPTEN_JoystickGetDeviceInstanceID(int device_index)
   293 {
   294     return JoystickByDeviceIndex(device_index)->device_instance;
   295 }
   296 
   297 /* Function to open a joystick for use.
   298    The joystick to open is specified by the device index.
   299    This should fill the nbuttons and naxes fields of the joystick structure.
   300    It returns 0, or -1 if there is an error.
   301  */
   302 static int
   303 EMSCRIPTEN_JoystickOpen(SDL_Joystick * joystick, int device_index)
   304 {
   305     SDL_joylist_item *item = JoystickByDeviceIndex(device_index);
   306 
   307     if (item == NULL ) {
   308         return SDL_SetError("No such device");
   309     }
   310 
   311     if (item->joystick != NULL) {
   312         return SDL_SetError("Joystick already opened");
   313     }
   314 
   315     joystick->instance_id = item->device_instance;
   316     joystick->hwdata = (struct joystick_hwdata *) item;
   317     item->joystick = joystick;
   318 
   319     /* HTML5 Gamepad API doesn't say anything about these */
   320     joystick->nhats = 0;
   321     joystick->nballs = 0;
   322 
   323     joystick->nbuttons = item->nbuttons;
   324     joystick->naxes = item->naxes;
   325 
   326     return (0);
   327 }
   328 
   329 /* Function to update the state of a joystick - called as a device poll.
   330  * This function shouldn't update the joystick structure directly,
   331  * but instead should call SDL_PrivateJoystick*() to deliver events
   332  * and update joystick device state.
   333  */
   334 static void
   335 EMSCRIPTEN_JoystickUpdate(SDL_Joystick * joystick)
   336 {
   337     EmscriptenGamepadEvent gamepadState;
   338     SDL_joylist_item *item = (SDL_joylist_item *) joystick->hwdata;
   339     int i, result, buttonState;
   340 
   341     emscripten_sample_gamepad_data();
   342 
   343     if (item) {
   344         result = emscripten_get_gamepad_status(item->index, &gamepadState);
   345         if( result == EMSCRIPTEN_RESULT_SUCCESS) {
   346             if(gamepadState.timestamp == 0 || gamepadState.timestamp != item->timestamp) {
   347                 for(i = 0; i < item->nbuttons; i++) {
   348                     if(item->digitalButton[i] != gamepadState.digitalButton[i]) {
   349                         buttonState = gamepadState.digitalButton[i]? SDL_PRESSED: SDL_RELEASED;
   350                         SDL_PrivateJoystickButton(item->joystick, i, buttonState);
   351                     }
   352 
   353                     /* store values to compare them in the next update */
   354                     item->analogButton[i] = gamepadState.analogButton[i];
   355                     item->digitalButton[i] = gamepadState.digitalButton[i];
   356                 }
   357 
   358                 for(i = 0; i < item->naxes; i++) {
   359                     if(item->axis[i] != gamepadState.axis[i]) {
   360                         /* do we need to do conversion? */
   361                         SDL_PrivateJoystickAxis(item->joystick, i,
   362                                                   (Sint16) (32767.*gamepadState.axis[i]));
   363                     }
   364 
   365                     /* store to compare in next update */
   366                     item->axis[i] = gamepadState.axis[i];
   367                 }
   368 
   369                 item->timestamp = gamepadState.timestamp;
   370             }
   371         }
   372     }
   373 }
   374 
   375 /* Function to close a joystick after use */
   376 static void
   377 EMSCRIPTEN_JoystickClose(SDL_Joystick * joystick)
   378 {
   379     SDL_joylist_item *item = (SDL_joylist_item *) joystick->hwdata;
   380     if (item) {
   381         item->joystick = NULL;
   382     }
   383 }
   384 
   385 static SDL_JoystickGUID
   386 EMSCRIPTEN_JoystickGetDeviceGUID(int device_index)
   387 {
   388     SDL_JoystickGUID guid;
   389     /* the GUID is just the first 16 chars of the name for now */
   390     const char *name = EMSCRIPTEN_JoystickGetDeviceName(device_index);
   391     SDL_zero(guid);
   392     SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
   393     return guid;
   394 }
   395 
   396 static int
   397 EMSCRIPTEN_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
   398 {
   399     return SDL_Unsupported();
   400 }
   401 
   402 SDL_JoystickDriver SDL_EMSCRIPTEN_JoystickDriver =
   403 {
   404     EMSCRIPTEN_JoystickInit,
   405     EMSCRIPTEN_JoystickGetCount,
   406     EMSCRIPTEN_JoystickDetect,
   407     EMSCRIPTEN_JoystickGetDeviceName,
   408     EMSCRIPTEN_JoystickGetDevicePlayerIndex,
   409     EMSCRIPTEN_JoystickGetDeviceGUID,
   410     EMSCRIPTEN_JoystickGetDeviceInstanceID,
   411     EMSCRIPTEN_JoystickOpen,
   412     EMSCRIPTEN_JoystickRumble,
   413     EMSCRIPTEN_JoystickUpdate,
   414     EMSCRIPTEN_JoystickClose,
   415     EMSCRIPTEN_JoystickQuit,
   416 };
   417 
   418 #endif /* SDL_JOYSTICK_EMSCRIPTEN */
   419 
   420 /* vi: set ts=4 sw=4 expandtab: */