src/joystick/emscripten/SDL_sysjoystick.c
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Wed, 18 Feb 2015 21:34:07 +0100
changeset 9357 620c3c2fb9b9
parent 9353 4528766f5d2c
child 9372 a9bf04588f17
permissions -rw-r--r--
Emscripten: Fixed receiving joystick events after failed init or subsystem quit.

The callbacks used to receive the HTML events were not removed if the joystick
subsystem initialization failed or if the joystick subsystem was quit. Also, the
already connected joysticks were not deleted if the initialization failed later.
icculus@9278
     1
/*
icculus@9278
     2
  Simple DirectMedia Layer
icculus@9278
     3
  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
icculus@9278
     4
icculus@9278
     5
  This software is provided 'as-is', without any express or implied
icculus@9278
     6
  warranty.  In no event will the authors be held liable for any damages
icculus@9278
     7
  arising from the use of this software.
icculus@9278
     8
icculus@9278
     9
  Permission is granted to anyone to use this software for any purpose,
icculus@9278
    10
  including commercial applications, and to alter it and redistribute it
icculus@9278
    11
  freely, subject to the following restrictions:
icculus@9278
    12
icculus@9278
    13
  1. The origin of this software must not be misrepresented; you must not
icculus@9278
    14
     claim that you wrote the original software. If you use this software
icculus@9278
    15
     in a product, an acknowledgment in the product documentation would be
icculus@9278
    16
     appreciated but is not required.
icculus@9278
    17
  2. Altered source versions must be plainly marked as such, and must not be
icculus@9278
    18
     misrepresented as being the original software.
icculus@9278
    19
  3. This notice may not be removed or altered from any source distribution.
icculus@9278
    20
*/
icculus@9278
    21
icculus@9278
    22
#include "../../SDL_internal.h"
icculus@9278
    23
icculus@9278
    24
#ifdef SDL_JOYSTICK_EMSCRIPTEN
icculus@9278
    25
icculus@9278
    26
#include <stdio.h>              /* For the definition of NULL */
icculus@9278
    27
#include "SDL_error.h"
icculus@9278
    28
#include "SDL_events.h"
icculus@9278
    29
icculus@9278
    30
#if !SDL_EVENTS_DISABLED
icculus@9278
    31
#include "../../events/SDL_events_c.h"
icculus@9278
    32
#endif
icculus@9278
    33
icculus@9278
    34
#include "SDL_joystick.h"
icculus@9278
    35
#include "SDL_hints.h"
icculus@9278
    36
#include "SDL_assert.h"
icculus@9278
    37
#include "SDL_timer.h"
icculus@9278
    38
#include "SDL_log.h"
icculus@9278
    39
#include "SDL_sysjoystick_c.h"
icculus@9278
    40
#include "../SDL_joystick_c.h"
icculus@9278
    41
icculus@9278
    42
static SDL_joylist_item * JoystickByIndex(int index);
icculus@9278
    43
icculus@9278
    44
static SDL_joylist_item *SDL_joylist = NULL;
icculus@9278
    45
static SDL_joylist_item *SDL_joylist_tail = NULL;
icculus@9278
    46
static int numjoysticks = 0;
icculus@9278
    47
static int instance_counter = 0;
icculus@9278
    48
philipp@9352
    49
EM_BOOL
icculus@9278
    50
Emscripten_JoyStickConnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
icculus@9278
    51
{
icculus@9278
    52
    int i;
icculus@9278
    53
icculus@9278
    54
    SDL_joylist_item *item;
icculus@9278
    55
icculus@9278
    56
    if (JoystickByIndex(gamepadEvent->index) != NULL) {
icculus@9278
    57
      return 1;
icculus@9278
    58
    }
icculus@9278
    59
icculus@9278
    60
#if !SDL_EVENTS_DISABLED
icculus@9278
    61
    SDL_Event event;
icculus@9278
    62
#endif
icculus@9278
    63
icculus@9278
    64
    item = (SDL_joylist_item *) SDL_malloc(sizeof (SDL_joylist_item));
icculus@9278
    65
    if (item == NULL) {
icculus@9278
    66
        return 1;
icculus@9278
    67
    }
icculus@9278
    68
icculus@9278
    69
    SDL_zerop(item);
icculus@9278
    70
    item->index = gamepadEvent->index;
icculus@9278
    71
icculus@9278
    72
    item->name = SDL_strdup(gamepadEvent->id);
icculus@9278
    73
    if ( item->name == NULL ) {
icculus@9278
    74
        SDL_free(item);
icculus@9278
    75
        return 1;
icculus@9278
    76
    }
icculus@9278
    77
icculus@9278
    78
    item->mapping = SDL_strdup(gamepadEvent->mapping);
icculus@9278
    79
    if ( item->mapping == NULL ) {
icculus@9278
    80
        SDL_free(item->name);
icculus@9278
    81
        SDL_free(item);
icculus@9278
    82
        return 1;
icculus@9278
    83
    }
icculus@9278
    84
icculus@9278
    85
    item->naxes = gamepadEvent->numAxes;
icculus@9278
    86
    item->nbuttons = gamepadEvent->numButtons;
icculus@9278
    87
    item->device_instance = instance_counter++;
icculus@9278
    88
icculus@9278
    89
    item->timestamp = gamepadEvent->timestamp;
icculus@9278
    90
icculus@9278
    91
    for( i = 0; i < item->naxes; i++) {
icculus@9278
    92
        item->axis[i] = gamepadEvent->axis[i];
icculus@9278
    93
    }
icculus@9278
    94
icculus@9278
    95
    for( i = 0; i < item->nbuttons; i++) {
icculus@9278
    96
        item->analogButton[i] = gamepadEvent->analogButton[i];
icculus@9278
    97
        item->digitalButton[i] = gamepadEvent->digitalButton[i];
icculus@9278
    98
    }
icculus@9278
    99
icculus@9278
   100
    if (SDL_joylist_tail == NULL) {
icculus@9278
   101
        SDL_joylist = SDL_joylist_tail = item;
icculus@9278
   102
    } else {
icculus@9278
   103
        SDL_joylist_tail->next = item;
icculus@9278
   104
        SDL_joylist_tail = item;
icculus@9278
   105
    }
icculus@9278
   106
icculus@9278
   107
    ++numjoysticks;
philipp@9345
   108
#ifdef DEBUG_JOYSTICK
philipp@9345
   109
    SDL_Log("Number of joysticks is %d", numjoysticks);
philipp@9345
   110
#endif
icculus@9278
   111
#if !SDL_EVENTS_DISABLED
icculus@9278
   112
    event.type = SDL_JOYDEVICEADDED;
icculus@9278
   113
icculus@9278
   114
    if (SDL_GetEventState(event.type) == SDL_ENABLE) {
philipp@9346
   115
        event.jdevice.which = item->index;
icculus@9278
   116
        if ( (SDL_EventOK == NULL) ||
icculus@9278
   117
             (*SDL_EventOK) (SDL_EventOKParam, &event) ) {
icculus@9278
   118
            SDL_PushEvent(&event);
icculus@9278
   119
        }
icculus@9278
   120
    }
icculus@9278
   121
#endif /* !SDL_EVENTS_DISABLED */
icculus@9278
   122
philipp@9345
   123
#ifdef DEBUG_JOYSTICK
icculus@9278
   124
    SDL_Log("Added joystick with index %d", item->index);
philipp@9345
   125
#endif
icculus@9278
   126
icculus@9278
   127
    return 1;
icculus@9278
   128
}
icculus@9278
   129
philipp@9352
   130
EM_BOOL
icculus@9278
   131
Emscripten_JoyStickDisconnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
icculus@9278
   132
{
icculus@9278
   133
    SDL_joylist_item *item = SDL_joylist;
icculus@9278
   134
    SDL_joylist_item *prev = NULL;
icculus@9278
   135
#if !SDL_EVENTS_DISABLED
icculus@9278
   136
    SDL_Event event;
icculus@9278
   137
#endif
icculus@9278
   138
icculus@9278
   139
    while (item != NULL) {
icculus@9278
   140
        if (item->index == gamepadEvent->index) {
icculus@9278
   141
            break;
icculus@9278
   142
        }
icculus@9278
   143
        prev = item;
icculus@9278
   144
        item = item->next;
icculus@9278
   145
    }
icculus@9278
   146
icculus@9278
   147
    if (item == NULL) {
icculus@9278
   148
        return 1;
icculus@9278
   149
    }
icculus@9278
   150
icculus@9278
   151
    if (item->joystick) {
icculus@9278
   152
        item->joystick->hwdata = NULL;
icculus@9278
   153
    }
icculus@9278
   154
icculus@9278
   155
    if (prev != NULL) {
icculus@9278
   156
        prev->next = item->next;
icculus@9278
   157
    } else {
icculus@9278
   158
        SDL_assert(SDL_joylist == item);
icculus@9278
   159
        SDL_joylist = item->next;
icculus@9278
   160
    }
icculus@9278
   161
    if (item == SDL_joylist_tail) {
icculus@9278
   162
        SDL_joylist_tail = prev;
icculus@9278
   163
    }
icculus@9278
   164
icculus@9278
   165
    /* Need to decrement the joystick count before we post the event */
icculus@9278
   166
    --numjoysticks;
icculus@9278
   167
icculus@9278
   168
#if !SDL_EVENTS_DISABLED
icculus@9278
   169
    event.type = SDL_JOYDEVICEREMOVED;
icculus@9278
   170
icculus@9278
   171
    if (SDL_GetEventState(event.type) == SDL_ENABLE) {
icculus@9278
   172
        event.jdevice.which = item->device_instance;
icculus@9278
   173
        if ( (SDL_EventOK == NULL) ||
icculus@9278
   174
             (*SDL_EventOK) (SDL_EventOKParam, &event) ) {
icculus@9278
   175
            SDL_PushEvent(&event);
icculus@9278
   176
        }
icculus@9278
   177
    }
icculus@9278
   178
#endif /* !SDL_EVENTS_DISABLED */
icculus@9278
   179
philipp@9345
   180
#ifdef DEBUG_JOYSTICK
philipp@9345
   181
    SDL_Log("Removed joystick with id %d", item->device_instance);
philipp@9345
   182
#endif
icculus@9278
   183
    SDL_free(item->name);
icculus@9278
   184
    SDL_free(item->mapping);
icculus@9278
   185
    SDL_free(item);
icculus@9278
   186
    return 1;
icculus@9278
   187
}
icculus@9278
   188
icculus@9278
   189
/* Function to scan the system for joysticks.
icculus@9278
   190
 * It should return 0, or -1 on an unrecoverable fatal error.
icculus@9278
   191
 */
icculus@9278
   192
int
icculus@9278
   193
SDL_SYS_JoystickInit(void)
icculus@9278
   194
{
icculus@9278
   195
    int retval, i, numjs;
icculus@9278
   196
    EmscriptenGamepadEvent gamepadState;
icculus@9278
   197
icculus@9278
   198
    numjoysticks = 0;
icculus@9278
   199
    numjs = emscripten_get_num_gamepads();
icculus@9278
   200
icculus@9278
   201
    /* Check if gamepad is supported by browser */
icculus@9278
   202
    if (numjs == EMSCRIPTEN_RESULT_NOT_SUPPORTED) {
icculus@9278
   203
        return -1;
icculus@9278
   204
    }
icculus@9278
   205
icculus@9278
   206
    /* handle already connected gamepads */
icculus@9278
   207
    if (numjs > 0) {
icculus@9278
   208
        for(i = 0; i < numjs; i++) {
icculus@9278
   209
            retval = emscripten_get_gamepad_status(i, &gamepadState);
icculus@9278
   210
            if (retval == EMSCRIPTEN_RESULT_SUCCESS) {
icculus@9278
   211
                Emscripten_JoyStickConnected(EMSCRIPTEN_EVENT_GAMEPADCONNECTED,
icculus@9278
   212
                                             &gamepadState,
icculus@9278
   213
                                             NULL);
icculus@9278
   214
            }
icculus@9278
   215
        }
icculus@9278
   216
    }
icculus@9278
   217
icculus@9278
   218
    retval = emscripten_set_gamepadconnected_callback(NULL,
icculus@9278
   219
                                                      0,
icculus@9278
   220
                                                      Emscripten_JoyStickConnected);
icculus@9278
   221
icculus@9278
   222
    if(retval != EMSCRIPTEN_RESULT_SUCCESS) {
philipp@9357
   223
        SDL_SYS_JoystickQuit();
icculus@9278
   224
        return -1;
icculus@9278
   225
    }
icculus@9278
   226
icculus@9278
   227
    retval = emscripten_set_gamepaddisconnected_callback(NULL,
icculus@9278
   228
                                                         0,
icculus@9278
   229
                                                         Emscripten_JoyStickDisconnected);
icculus@9278
   230
    if(retval != EMSCRIPTEN_RESULT_SUCCESS) {
philipp@9357
   231
        SDL_SYS_JoystickQuit();
icculus@9278
   232
        return -1;
icculus@9278
   233
    }
icculus@9278
   234
icculus@9278
   235
    return 0;
icculus@9278
   236
}
icculus@9278
   237
icculus@9278
   238
static SDL_joylist_item *
icculus@9278
   239
JoystickByIndex(int index)
icculus@9278
   240
{
icculus@9278
   241
    SDL_joylist_item *item = SDL_joylist;
icculus@9278
   242
icculus@9278
   243
    if (index < 0) {
icculus@9278
   244
        return NULL;
icculus@9278
   245
    }
icculus@9278
   246
icculus@9278
   247
    while (item != NULL) {
icculus@9278
   248
        if (item->index == index) {
icculus@9278
   249
            break;
icculus@9278
   250
        }
icculus@9278
   251
        item = item->next;
icculus@9278
   252
    }
icculus@9278
   253
icculus@9278
   254
    return item;
icculus@9278
   255
}
icculus@9278
   256
icculus@9278
   257
int SDL_SYS_NumJoysticks()
icculus@9278
   258
{
icculus@9278
   259
    return numjoysticks;
icculus@9278
   260
}
icculus@9278
   261
icculus@9278
   262
void SDL_SYS_JoystickDetect()
icculus@9278
   263
{
icculus@9278
   264
}
icculus@9278
   265
icculus@9278
   266
/* Function to get the device-dependent name of a joystick */
icculus@9278
   267
const char *
icculus@9278
   268
SDL_SYS_JoystickNameForDeviceIndex(int index)
icculus@9278
   269
{
icculus@9278
   270
    SDL_joylist_item *item = JoystickByIndex(index);
icculus@9278
   271
    if (item == NULL) {
icculus@9278
   272
        SDL_SetError("Joystick with index %d not found", index);
icculus@9278
   273
        return NULL;
icculus@9278
   274
    }
icculus@9278
   275
icculus@9278
   276
    return item->name;
icculus@9278
   277
}
icculus@9278
   278
icculus@9278
   279
/* Function to perform the mapping from device index to the instance id for this index */
icculus@9278
   280
SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int index)
icculus@9278
   281
{
icculus@9278
   282
    SDL_joylist_item *item = JoystickByIndex(index);
icculus@9278
   283
    if (item == NULL) {
icculus@9278
   284
        SDL_SetError("Joystick with index %d not found", index);
icculus@9300
   285
        return 0;
icculus@9278
   286
    }
icculus@9278
   287
icculus@9278
   288
    return item->device_instance;
icculus@9278
   289
}
icculus@9278
   290
icculus@9278
   291
/* Function to open a joystick for use.
icculus@9278
   292
   The joystick to open is specified by the index field of the joystick.
icculus@9278
   293
   This should fill the nbuttons and naxes fields of the joystick structure.
icculus@9278
   294
   It returns 0, or -1 if there is an error.
icculus@9278
   295
 */
icculus@9278
   296
int
icculus@9278
   297
SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int index)
icculus@9278
   298
{
icculus@9278
   299
    SDL_joylist_item *item = JoystickByIndex(index);
icculus@9278
   300
icculus@9278
   301
    if (item == NULL ) {
icculus@9278
   302
        return SDL_SetError("No such device");
icculus@9278
   303
    }
icculus@9278
   304
icculus@9278
   305
    if (item->joystick != NULL) {
icculus@9278
   306
        return SDL_SetError("Joystick already opened");
icculus@9278
   307
    }
icculus@9278
   308
icculus@9278
   309
    joystick->instance_id = item->device_instance;
icculus@9278
   310
    joystick->hwdata = (struct joystick_hwdata *) item;
icculus@9278
   311
    item->joystick = joystick;
icculus@9278
   312
icculus@9278
   313
    /* HTML5 Gamepad API doesn't say anything about these */
icculus@9278
   314
    joystick->nhats = 0;
icculus@9278
   315
    joystick->nballs = 0;
icculus@9278
   316
icculus@9278
   317
    joystick->nbuttons = item->nbuttons;
icculus@9278
   318
    joystick->naxes = item->naxes;
icculus@9278
   319
icculus@9278
   320
    return (0);
icculus@9278
   321
}
icculus@9278
   322
icculus@9278
   323
/* Function to determine is this joystick is attached to the system right now */
icculus@9278
   324
SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
icculus@9278
   325
{
icculus@9278
   326
    return !joystick->closed && (joystick->hwdata != NULL);
icculus@9278
   327
}
icculus@9278
   328
icculus@9278
   329
/* Function to update the state of a joystick - called as a device poll.
icculus@9278
   330
 * This function shouldn't update the joystick structure directly,
icculus@9278
   331
 * but instead should call SDL_PrivateJoystick*() to deliver events
icculus@9278
   332
 * and update joystick device state.
icculus@9278
   333
 */
icculus@9278
   334
void
icculus@9278
   335
SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
icculus@9278
   336
{
icculus@9278
   337
    EmscriptenGamepadEvent gamepadState;
icculus@9278
   338
    SDL_joylist_item *item = SDL_joylist;
philipp@9291
   339
    int i, result, buttonState;
icculus@9278
   340
icculus@9278
   341
    while (item != NULL) {
icculus@9278
   342
        result = emscripten_get_gamepad_status(item->index, &gamepadState);
icculus@9278
   343
        if( result == EMSCRIPTEN_RESULT_SUCCESS) {
icculus@9278
   344
            if(gamepadState.timestamp == 0 || gamepadState.timestamp != item->timestamp) {
icculus@9278
   345
                for(i = 0; i < item->nbuttons; i++) {
icculus@9278
   346
                    if(item->digitalButton[i] != gamepadState.digitalButton[i]) {
icculus@9278
   347
                        buttonState = gamepadState.digitalButton[i]? SDL_PRESSED: SDL_RELEASED;
icculus@9278
   348
                        SDL_PrivateJoystickButton(item->joystick, i, buttonState);
icculus@9278
   349
                    }
icculus@9278
   350
                }
icculus@9278
   351
icculus@9278
   352
                for(i = 0; i < item->naxes; i++) {
icculus@9278
   353
                    if(item->axis[i] != gamepadState.axis[i]) {
icculus@9278
   354
                        // do we need to do conversion?
icculus@9278
   355
                        SDL_PrivateJoystickAxis(item->joystick, i,
icculus@9278
   356
                                                  (Sint16) (32767.*gamepadState.axis[i]));
icculus@9278
   357
                    }
icculus@9278
   358
                }
icculus@9278
   359
icculus@9278
   360
                item->timestamp = gamepadState.timestamp;
icculus@9278
   361
                for( i = 0; i < item->naxes; i++) {
icculus@9278
   362
                    item->axis[i] = gamepadState.axis[i];
icculus@9278
   363
                }
icculus@9278
   364
icculus@9278
   365
                for( i = 0; i < item->nbuttons; i++) {
icculus@9278
   366
                    item->analogButton[i] = gamepadState.analogButton[i];
icculus@9278
   367
                    item->digitalButton[i] = gamepadState.digitalButton[i];
icculus@9278
   368
                }
icculus@9278
   369
            }
icculus@9278
   370
        }
icculus@9278
   371
        item = item->next;
icculus@9278
   372
    }
icculus@9278
   373
}
icculus@9278
   374
icculus@9278
   375
/* Function to close a joystick after use */
icculus@9278
   376
void
icculus@9278
   377
SDL_SYS_JoystickClose(SDL_Joystick * joystick)
icculus@9278
   378
{
icculus@9278
   379
    if (joystick->hwdata) {
icculus@9278
   380
        ((SDL_joylist_item*)joystick->hwdata)->joystick = NULL;
icculus@9278
   381
        joystick->hwdata = NULL;
icculus@9278
   382
    }
icculus@9278
   383
    joystick->closed = 1;
icculus@9278
   384
}
icculus@9278
   385
icculus@9278
   386
/* Function to perform any system-specific joystick related cleanup */
icculus@9278
   387
void
icculus@9278
   388
SDL_SYS_JoystickQuit(void)
icculus@9278
   389
{
icculus@9278
   390
    SDL_joylist_item *item = NULL;
icculus@9278
   391
    SDL_joylist_item *next = NULL;
icculus@9278
   392
icculus@9278
   393
    for (item = SDL_joylist; item; item = next) {
icculus@9278
   394
        next = item->next;
icculus@9278
   395
        SDL_free(item->mapping);
icculus@9278
   396
        SDL_free(item->name);
icculus@9278
   397
        SDL_free(item);
icculus@9278
   398
    }
icculus@9278
   399
icculus@9278
   400
    SDL_joylist = SDL_joylist_tail = NULL;
icculus@9278
   401
icculus@9278
   402
    numjoysticks = 0;
icculus@9278
   403
    instance_counter = 0;
philipp@9357
   404
philipp@9357
   405
    emscripten_set_gamepadconnected_callback(NULL, 0, NULL);
philipp@9357
   406
    emscripten_set_gamepaddisconnected_callback(NULL, 0, NULL);
icculus@9278
   407
}
icculus@9278
   408
icculus@9278
   409
SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID(int index)
icculus@9278
   410
{
icculus@9278
   411
    SDL_JoystickGUID guid;
icculus@9278
   412
    /* the GUID is just the first 16 chars of the name for now */
icculus@9278
   413
    const char *name = SDL_SYS_JoystickNameForDeviceIndex(index);
icculus@9278
   414
    SDL_zero(guid);
icculus@9278
   415
    SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
icculus@9278
   416
    return guid;
icculus@9278
   417
}
icculus@9278
   418
icculus@9278
   419
icculus@9278
   420
SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
icculus@9278
   421
{
icculus@9278
   422
    SDL_JoystickGUID guid;
icculus@9278
   423
    /* the GUID is just the first 16 chars of the name for now */
icculus@9278
   424
    const char *name = joystick->name;
icculus@9278
   425
    SDL_zero(guid);
icculus@9278
   426
    SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
icculus@9278
   427
    return guid;
icculus@9278
   428
}
icculus@9278
   429
icculus@9278
   430
#endif /* SDL_JOYSTICK_EMSCRIPTEN */