src/joystick/emscripten/SDL_sysjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 04 Jan 2019 22:01:14 -0800
changeset 12503 806492103856
parent 12475 0ae268b6ddf0
child 12575 62f907f026bb
permissions -rw-r--r--
Updated copyright for 2019
     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     numjs = emscripten_get_num_gamepads();
   193 
   194     /* Check if gamepad is supported by browser */
   195     if (numjs == EMSCRIPTEN_RESULT_NOT_SUPPORTED) {
   196         return SDL_SetError("Gamepads not supported");
   197     }
   198 
   199     /* handle already connected gamepads */
   200     if (numjs > 0) {
   201         for(i = 0; i < numjs; i++) {
   202             retval = emscripten_get_gamepad_status(i, &gamepadState);
   203             if (retval == EMSCRIPTEN_RESULT_SUCCESS) {
   204                 Emscripten_JoyStickConnected(EMSCRIPTEN_EVENT_GAMEPADCONNECTED,
   205                                              &gamepadState,
   206                                              NULL);
   207             }
   208         }
   209     }
   210 
   211     retval = emscripten_set_gamepadconnected_callback(NULL,
   212                                                       0,
   213                                                       Emscripten_JoyStickConnected);
   214 
   215     if(retval != EMSCRIPTEN_RESULT_SUCCESS) {
   216         EMSCRIPTEN_JoystickQuit();
   217         return SDL_SetError("Could not set gamepad connect callback");
   218     }
   219 
   220     retval = emscripten_set_gamepaddisconnected_callback(NULL,
   221                                                          0,
   222                                                          Emscripten_JoyStickDisconnected);
   223     if(retval != EMSCRIPTEN_RESULT_SUCCESS) {
   224         EMSCRIPTEN_JoystickQuit();
   225         return SDL_SetError("Could not set gamepad disconnect callback");
   226     }
   227 
   228     return 0;
   229 }
   230 
   231 /* Returns item matching given SDL device index. */
   232 static SDL_joylist_item *
   233 JoystickByDeviceIndex(int device_index)
   234 {
   235     SDL_joylist_item *item = SDL_joylist;
   236 
   237     while (0 < device_index) {
   238         --device_index;
   239         item = item->next;
   240     }
   241 
   242     return item;
   243 }
   244 
   245 /* Returns item matching given HTML gamepad index. */
   246 static SDL_joylist_item *
   247 JoystickByIndex(int index)
   248 {
   249     SDL_joylist_item *item = SDL_joylist;
   250 
   251     if (index < 0) {
   252         return NULL;
   253     }
   254 
   255     while (item != NULL) {
   256         if (item->index == index) {
   257             break;
   258         }
   259         item = item->next;
   260     }
   261 
   262     return item;
   263 }
   264 
   265 static int
   266 EMSCRIPTEN_JoystickGetCount(void)
   267 {
   268     return numjoysticks;
   269 }
   270 
   271 static void
   272 EMSCRIPTEN_JoystickDetect(void)
   273 {
   274 }
   275 
   276 static const char *
   277 EMSCRIPTEN_JoystickGetDeviceName(int device_index)
   278 {
   279     return JoystickByDeviceIndex(device_index)->name;
   280 }
   281 
   282 static int
   283 EMSCRIPTEN_JoystickGetDevicePlayerIndex(int device_index)
   284 {
   285     return -1;
   286 }
   287 
   288 static SDL_JoystickID
   289 EMSCRIPTEN_JoystickGetDeviceInstanceID(int device_index)
   290 {
   291     return JoystickByDeviceIndex(device_index)->device_instance;
   292 }
   293 
   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.
   298  */
   299 static int
   300 EMSCRIPTEN_JoystickOpen(SDL_Joystick * joystick, int device_index)
   301 {
   302     SDL_joylist_item *item = JoystickByDeviceIndex(device_index);
   303 
   304     if (item == NULL ) {
   305         return SDL_SetError("No such device");
   306     }
   307 
   308     if (item->joystick != NULL) {
   309         return SDL_SetError("Joystick already opened");
   310     }
   311 
   312     joystick->instance_id = item->device_instance;
   313     joystick->hwdata = (struct joystick_hwdata *) item;
   314     item->joystick = joystick;
   315 
   316     /* HTML5 Gamepad API doesn't say anything about these */
   317     joystick->nhats = 0;
   318     joystick->nballs = 0;
   319 
   320     joystick->nbuttons = item->nbuttons;
   321     joystick->naxes = item->naxes;
   322 
   323     return (0);
   324 }
   325 
   326 /* Function to update the state of a joystick - called as a device poll.
   327  * This function shouldn't update the joystick structure directly,
   328  * but instead should call SDL_PrivateJoystick*() to deliver events
   329  * and update joystick device state.
   330  */
   331 static void
   332 EMSCRIPTEN_JoystickUpdate(SDL_Joystick * joystick)
   333 {
   334     EmscriptenGamepadEvent gamepadState;
   335     SDL_joylist_item *item = (SDL_joylist_item *) joystick->hwdata;
   336     int i, result, buttonState;
   337 
   338     if (item) {
   339         result = emscripten_get_gamepad_status(item->index, &gamepadState);
   340         if( result == EMSCRIPTEN_RESULT_SUCCESS) {
   341             if(gamepadState.timestamp == 0 || gamepadState.timestamp != item->timestamp) {
   342                 for(i = 0; i < item->nbuttons; i++) {
   343                     if(item->digitalButton[i] != gamepadState.digitalButton[i]) {
   344                         buttonState = gamepadState.digitalButton[i]? SDL_PRESSED: SDL_RELEASED;
   345                         SDL_PrivateJoystickButton(item->joystick, i, buttonState);
   346                     }
   347 
   348                     /* store values to compare them in the next update */
   349                     item->analogButton[i] = gamepadState.analogButton[i];
   350                     item->digitalButton[i] = gamepadState.digitalButton[i];
   351                 }
   352 
   353                 for(i = 0; i < item->naxes; i++) {
   354                     if(item->axis[i] != gamepadState.axis[i]) {
   355                         /* do we need to do conversion? */
   356                         SDL_PrivateJoystickAxis(item->joystick, i,
   357                                                   (Sint16) (32767.*gamepadState.axis[i]));
   358                     }
   359 
   360                     /* store to compare in next update */
   361                     item->axis[i] = gamepadState.axis[i];
   362                 }
   363 
   364                 item->timestamp = gamepadState.timestamp;
   365             }
   366         }
   367     }
   368 }
   369 
   370 /* Function to close a joystick after use */
   371 static void
   372 EMSCRIPTEN_JoystickClose(SDL_Joystick * joystick)
   373 {
   374     SDL_joylist_item *item = (SDL_joylist_item *) joystick->hwdata;
   375     if (item) {
   376         item->joystick = NULL;
   377     }
   378 }
   379 
   380 static SDL_JoystickGUID
   381 EMSCRIPTEN_JoystickGetDeviceGUID(int device_index)
   382 {
   383     SDL_JoystickGUID guid;
   384     /* the GUID is just the first 16 chars of the name for now */
   385     const char *name = EMSCRIPTEN_JoystickGetDeviceName(device_index);
   386     SDL_zero(guid);
   387     SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
   388     return guid;
   389 }
   390 
   391 static int
   392 EMSCRIPTEN_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
   393 {
   394     return SDL_Unsupported();
   395 }
   396 
   397 SDL_JoystickDriver SDL_EMSCRIPTEN_JoystickDriver =
   398 {
   399     EMSCRIPTEN_JoystickInit,
   400     EMSCRIPTEN_JoystickGetCount,
   401     EMSCRIPTEN_JoystickDetect,
   402     EMSCRIPTEN_JoystickGetDeviceName,
   403     EMSCRIPTEN_JoystickGetDevicePlayerIndex,
   404     EMSCRIPTEN_JoystickGetDeviceGUID,
   405     EMSCRIPTEN_JoystickGetDeviceInstanceID,
   406     EMSCRIPTEN_JoystickOpen,
   407     EMSCRIPTEN_JoystickRumble,
   408     EMSCRIPTEN_JoystickUpdate,
   409     EMSCRIPTEN_JoystickClose,
   410     EMSCRIPTEN_JoystickQuit,
   411 };
   412 
   413 #endif /* SDL_JOYSTICK_EMSCRIPTEN */
   414 
   415 /* vi: set ts=4 sw=4 expandtab: */