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