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