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