Atlas - SDL_haptic.c
Home / ext / SDL / src / haptic Lines: 1 | Size: 23825 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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#include "SDL_internal.h" 22 23#include "SDL_syshaptic.h" 24#ifdef SDL_JOYSTICK_HIDAPI 25#include "hidapi/SDL_hidapihaptic.h" 26#endif 27#include "SDL_haptic_c.h" 28#include "../joystick/SDL_joystick_c.h" // For SDL_IsJoystickValid 29#include "../SDL_hints_c.h" 30 31typedef struct SDL_Haptic_VIDPID_Naxes { 32 Uint16 vid; 33 Uint16 pid; 34 Uint16 naxes; 35} SDL_Haptic_VIDPID_Naxes; 36 37static void SDL_Haptic_Load_Axes_List(SDL_Haptic_VIDPID_Naxes **entries, int *num_entries) 38{ 39 SDL_Haptic_VIDPID_Naxes entry; 40 const char *spot; 41 int length = 0; 42 43 spot = SDL_GetHint(SDL_HINT_JOYSTICK_HAPTIC_AXES); 44 if (!spot) 45 return; 46 47 while (SDL_sscanf(spot, "0x%hx/0x%hx/%hu%n", &entry.vid, &entry.pid, &entry.naxes, &length) == 3) { 48 SDL_assert(length > 0); 49 spot += length; 50 length = 0; 51 52 if ((*num_entries % 8) == 0) { 53 int new_max = *num_entries + 8; 54 SDL_Haptic_VIDPID_Naxes *new_entries = 55 (SDL_Haptic_VIDPID_Naxes *)SDL_realloc(*entries, new_max * sizeof(**entries)); 56 57 // Out of memory, go with what we have already 58 if (!new_entries) 59 break; 60 61 *entries = new_entries; 62 } 63 (*entries)[(*num_entries)++] = entry; 64 65 if (spot[0] == ',') 66 spot++; 67 } 68} 69 70// /* Return -1 if not found */ 71static int SDL_Haptic_Naxes_List_Index(struct SDL_Haptic_VIDPID_Naxes *entries, int num_entries, Uint16 vid, Uint16 pid) 72{ 73 if (!entries) 74 return -1; 75 76 int i; 77 for (i = 0; i < num_entries; ++i) { 78 if (entries[i].vid == vid && entries[i].pid == pid) 79 return i; 80 } 81 82 return -1; 83} 84 85// Check if device needs a custom number of naxes 86static int SDL_Haptic_Get_Naxes(Uint16 vid, Uint16 pid) 87{ 88 int num_entries = 0, index = 0, naxes = -1; 89 SDL_Haptic_VIDPID_Naxes *naxes_list = NULL; 90 91 SDL_Haptic_Load_Axes_List(&naxes_list, &num_entries); 92 if (!num_entries || !naxes_list) 93 return -1; 94 95 // Perform "wildcard" pass 96 index = SDL_Haptic_Naxes_List_Index(naxes_list, num_entries, 0xffff, 0xffff); 97 if (index >= 0) 98 naxes = naxes_list[index].naxes; 99 100 index = SDL_Haptic_Naxes_List_Index(naxes_list, num_entries, vid, pid); 101 if (index >= 0) 102 naxes = naxes_list[index].naxes; 103 104 SDL_free(naxes_list); 105 return naxes; 106} 107 108static SDL_Haptic *SDL_haptics = NULL; 109 110#define CHECK_HAPTIC_MAGIC(haptic, result) \ 111 CHECK_PARAM(!SDL_ObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC)) { \ 112 SDL_InvalidParamError("haptic"); \ 113 return result; \ 114 } 115 116bool SDL_InitHaptics(void) 117{ 118 if (!SDL_SYS_HapticInit()) { 119 return false; 120 } 121 #ifdef SDL_JOYSTICK_HIDAPI 122 if (!SDL_HIDAPI_HapticInit()) { 123 SDL_SYS_HapticQuit(); 124 return false; 125 } 126 #endif 127 128 return true; 129} 130 131static bool SDL_GetHapticIndex(SDL_HapticID instance_id, int *driver_index) 132{ 133 int num_haptics, device_index; 134 135 if (instance_id > 0) { 136 num_haptics = SDL_SYS_NumHaptics(); 137 for (device_index = 0; device_index < num_haptics; ++device_index) { 138 SDL_HapticID haptic_id = SDL_SYS_HapticInstanceID(device_index); 139 if (haptic_id == instance_id) { 140 *driver_index = device_index; 141 return true; 142 } 143 } 144 } 145 146 SDL_SetError("Haptic device %" SDL_PRIu32 " not found", instance_id); 147 return false; 148} 149 150SDL_HapticID *SDL_GetHaptics(int *count) 151{ 152 int device_index; 153 int haptic_index = 0, num_haptics = 0; 154 SDL_HapticID *haptics; 155 156 num_haptics = SDL_SYS_NumHaptics(); 157 158 haptics = (SDL_HapticID *)SDL_malloc((num_haptics + 1) * sizeof(*haptics)); 159 if (haptics) { 160 if (count) { 161 *count = num_haptics; 162 } 163 164 for (device_index = 0; device_index < num_haptics; ++device_index) { 165 haptics[haptic_index] = SDL_SYS_HapticInstanceID(device_index); 166 SDL_assert(haptics[haptic_index] > 0); 167 ++haptic_index; 168 } 169 haptics[haptic_index] = 0; 170 } else { 171 if (count) { 172 *count = 0; 173 } 174 } 175 176 return haptics; 177} 178 179const char *SDL_GetHapticNameForID(SDL_HapticID instance_id) 180{ 181 int device_index; 182 const char *name = NULL; 183 184 if (SDL_GetHapticIndex(instance_id, &device_index)) { 185 name = SDL_GetPersistentString(SDL_SYS_HapticName(device_index)); 186 } 187 return name; 188} 189 190SDL_Haptic *SDL_OpenHaptic(SDL_HapticID instance_id) 191{ 192 SDL_Haptic *haptic; 193 SDL_Haptic *hapticlist; 194 const char *name; 195 int device_index = 0; 196 197 if (!SDL_GetHapticIndex(instance_id, &device_index)) { 198 return NULL; 199 } 200 201 hapticlist = SDL_haptics; 202 /* If the haptic device is already open, return it 203 * it is important that we have a single haptic device for each instance id 204 */ 205 while (hapticlist) { 206 if (instance_id == hapticlist->instance_id) { 207 haptic = hapticlist; 208 ++haptic->ref_count; 209 return haptic; 210 } 211 hapticlist = hapticlist->next; 212 } 213 214 // Create the haptic device 215 haptic = (SDL_Haptic *)SDL_calloc(1, sizeof(*haptic)); 216 if (!haptic) { 217 return NULL; 218 } 219 220 // Initialize the haptic device 221 haptic->instance_id = instance_id; 222 haptic->rumble_id = -1; 223 if (!SDL_SYS_HapticOpen(haptic)) { 224 SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, false); 225 SDL_free(haptic); 226 return NULL; 227 } 228 229 if (!haptic->name) { 230 name = SDL_SYS_HapticName(device_index); 231 if (name) { 232 haptic->name = SDL_strdup(name); 233 } 234 } 235 236 // Add haptic to list 237 ++haptic->ref_count; 238 // Link the haptic in the list 239 haptic->next = SDL_haptics; 240 SDL_haptics = haptic; 241 242 SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, true); 243 244 // Disable autocenter and set gain to max. 245 if (haptic->supported & SDL_HAPTIC_GAIN) { 246 SDL_SetHapticGain(haptic, 100); 247 } 248 if (haptic->supported & SDL_HAPTIC_AUTOCENTER) { 249 SDL_SetHapticAutocenter(haptic, 0); 250 } 251 252 return haptic; 253} 254 255SDL_Haptic *SDL_GetHapticFromID(SDL_HapticID instance_id) 256{ 257 SDL_Haptic *haptic; 258 259 for (haptic = SDL_haptics; haptic; haptic = haptic->next) { 260 if (instance_id == haptic->instance_id) { 261 break; 262 } 263 } 264 return haptic; 265} 266 267SDL_HapticID SDL_GetHapticID(SDL_Haptic *haptic) 268{ 269 CHECK_HAPTIC_MAGIC(haptic, 0); 270 271 return haptic->instance_id; 272} 273 274const char *SDL_GetHapticName(SDL_Haptic *haptic) 275{ 276 CHECK_HAPTIC_MAGIC(haptic, NULL); 277 278 return SDL_GetPersistentString(haptic->name); 279} 280 281bool SDL_IsMouseHaptic(void) 282{ 283 if (SDL_SYS_HapticMouse() < 0) { 284 return false; 285 } 286 return true; 287} 288 289SDL_Haptic *SDL_OpenHapticFromMouse(void) 290{ 291 int device_index; 292 293 device_index = SDL_SYS_HapticMouse(); 294 295 if (device_index < 0) { 296 SDL_SetError("Haptic: Mouse isn't a haptic device."); 297 return NULL; 298 } 299 300 return SDL_OpenHaptic(device_index); 301} 302 303bool SDL_IsJoystickHaptic(SDL_Joystick *joystick) 304{ 305 bool result = false; 306 307 SDL_LockJoysticks(); 308 { 309 // Must be a valid joystick 310 if (SDL_IsJoystickValid(joystick) && 311 !SDL_IsGamepad(SDL_GetJoystickID(joystick))) { 312 #ifdef SDL_JOYSTICK_HIDAPI 313 result = SDL_SYS_JoystickIsHaptic(joystick) || SDL_HIDAPI_JoystickIsHaptic(joystick); 314 #else 315 result = SDL_SYS_JoystickIsHaptic(joystick); 316 #endif 317 } 318 } 319 SDL_UnlockJoysticks(); 320 321 return result; 322} 323 324SDL_Haptic *SDL_OpenHapticFromJoystick(SDL_Joystick *joystick) 325{ 326 SDL_Haptic *haptic; 327 SDL_Haptic *hapticlist; 328 329 SDL_LockJoysticks(); 330 { 331 // Joystick must be valid and haptic 332 if (!SDL_IsJoystickHaptic(joystick)) { 333 SDL_SetError("Haptic: Joystick isn't a haptic device."); 334 SDL_UnlockJoysticks(); 335 return NULL; 336 } 337 338 hapticlist = SDL_haptics; 339 // Check to see if joystick's haptic is already open 340 while (hapticlist) { 341 #ifdef SDL_JOYSTICK_HIDAPI 342 if (SDL_SYS_JoystickSameHaptic(hapticlist, joystick) || SDL_HIDAPI_JoystickSameHaptic(hapticlist, joystick)) { 343 #else 344 if (SDL_SYS_JoystickSameHaptic(hapticlist, joystick)) { 345 #endif 346 haptic = hapticlist; 347 ++haptic->ref_count; 348 SDL_UnlockJoysticks(); 349 return haptic; 350 } 351 hapticlist = hapticlist->next; 352 } 353 354 // Create the haptic device 355 haptic = (SDL_Haptic *)SDL_calloc(1, sizeof(*haptic)); 356 if (!haptic) { 357 SDL_UnlockJoysticks(); 358 return NULL; 359 } 360 361 /* Initialize the haptic device 362 * This function should fill in the instance ID and name. 363 */ 364 SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, true); 365 haptic->rumble_id = -1; 366 #ifdef SDL_JOYSTICK_HIDAPI 367 if (SDL_HIDAPI_JoystickIsHaptic(joystick)) { 368 if (!SDL_HIDAPI_HapticOpenFromJoystick(haptic, joystick)) { 369 SDL_SetError("Haptic: SDL_HIDAPI_HapticOpenFromJoystick failed."); 370 SDL_free(haptic); 371 SDL_UnlockJoysticks(); 372 return NULL; 373 } 374 } else 375 #endif 376 if (!SDL_SYS_HapticOpenFromJoystick(haptic, joystick)) { 377 SDL_SetError("Haptic: SDL_SYS_HapticOpenFromJoystick failed."); 378 SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, false); 379 SDL_free(haptic); 380 SDL_UnlockJoysticks(); 381 return NULL; 382 } 383 SDL_assert(haptic->instance_id != 0); 384 } 385 SDL_UnlockJoysticks(); 386 387 // Check if custom number of haptic axes was defined 388 Uint16 vid = SDL_GetJoystickVendor(joystick); 389 Uint16 pid = SDL_GetJoystickProduct(joystick); 390 int general_axes = SDL_GetNumJoystickAxes(joystick); 391 392 int naxes = SDL_Haptic_Get_Naxes(vid, pid); 393 if (naxes > 0) 394 haptic->naxes = naxes; 395 396 // Limit to the actual number of axes found on the device 397 if (general_axes >= 0 && naxes > general_axes) 398 haptic->naxes = general_axes; 399 400 // Add haptic to list 401 ++haptic->ref_count; 402 // Link the haptic in the list 403 haptic->next = SDL_haptics; 404 SDL_haptics = haptic; 405 406 SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, true); 407 408 // Disable autocenter and set gain to max. 409 if (haptic->supported & SDL_HAPTIC_GAIN) { 410 SDL_SetHapticGain(haptic, 100); 411 } 412 if (haptic->supported & SDL_HAPTIC_AUTOCENTER) { 413 SDL_SetHapticAutocenter(haptic, 0); 414 } 415 416 return haptic; 417} 418 419void SDL_CloseHaptic(SDL_Haptic *haptic) 420{ 421 SDL_HapticEffectID i; 422 SDL_Haptic *hapticlist; 423 SDL_Haptic *hapticlistprev; 424 425 CHECK_HAPTIC_MAGIC(haptic,); 426 427 // Check if it's still in use 428 if (--haptic->ref_count > 0) { 429 return; 430 } 431 432 #ifdef SDL_JOYSTICK_HIDAPI 433 if (SDL_HIDAPI_HapticIsHidapi(haptic)) { 434 SDL_HIDAPI_HapticClose(haptic); 435 } else 436 #endif 437 { 438 // Close it, properly removing effects if needed 439 for (i = 0; i < haptic->neffects; i++) { 440 if (haptic->effects[i].hweffect != NULL) { 441 SDL_DestroyHapticEffect(haptic, i); 442 } 443 } 444 SDL_SYS_HapticClose(haptic); 445 } 446 SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, false); 447 448 // Remove from the list 449 hapticlist = SDL_haptics; 450 hapticlistprev = NULL; 451 while (hapticlist) { 452 if (haptic == hapticlist) { 453 if (hapticlistprev) { 454 // unlink this entry 455 hapticlistprev->next = hapticlist->next; 456 } else { 457 SDL_haptics = haptic->next; 458 } 459 460 break; 461 } 462 hapticlistprev = hapticlist; 463 hapticlist = hapticlist->next; 464 } 465 466 // Free the data associated with this device 467 SDL_free(haptic->name); 468 SDL_free(haptic); 469} 470 471void SDL_QuitHaptics(void) 472{ 473 while (SDL_haptics) { 474 SDL_CloseHaptic(SDL_haptics); 475 } 476 477 #ifdef SDL_JOYSTICK_HIDAPI 478 SDL_HIDAPI_HapticQuit(); 479 #endif 480 SDL_SYS_HapticQuit(); 481} 482 483int SDL_GetMaxHapticEffects(SDL_Haptic *haptic) 484{ 485 CHECK_HAPTIC_MAGIC(haptic, -1); 486 487 return haptic->neffects; 488} 489 490int SDL_GetMaxHapticEffectsPlaying(SDL_Haptic *haptic) 491{ 492 CHECK_HAPTIC_MAGIC(haptic, -1); 493 494 return haptic->nplaying; 495} 496 497Uint32 SDL_GetHapticFeatures(SDL_Haptic *haptic) 498{ 499 CHECK_HAPTIC_MAGIC(haptic, 0); 500 501 return haptic->supported; 502} 503 504int SDL_GetNumHapticAxes(SDL_Haptic *haptic) 505{ 506 CHECK_HAPTIC_MAGIC(haptic, -1); 507 508 return haptic->naxes; 509} 510 511bool SDL_HapticEffectSupported(SDL_Haptic *haptic, const SDL_HapticEffect *effect) 512{ 513 CHECK_HAPTIC_MAGIC(haptic, false); 514 515 CHECK_PARAM(!effect) { 516 return false; 517 } 518 519 if ((haptic->supported & effect->type) != 0) { 520 return true; 521 } 522 return false; 523} 524 525SDL_HapticEffectID SDL_CreateHapticEffect(SDL_Haptic *haptic, const SDL_HapticEffect *effect) 526{ 527 SDL_HapticEffectID i; 528 529 CHECK_HAPTIC_MAGIC(haptic, -1); 530 531 CHECK_PARAM(!effect) { 532 SDL_InvalidParamError("effect"); 533 return -1; 534 } 535 536 // Check to see if effect is supported 537 if (SDL_HapticEffectSupported(haptic, effect) == false) { 538 SDL_SetError("Haptic: Effect not supported by haptic device."); 539 return -1; 540 } 541 542 #ifdef SDL_JOYSTICK_HIDAPI 543 if (SDL_HIDAPI_HapticIsHidapi(haptic)) { 544 return SDL_HIDAPI_HapticNewEffect(haptic, effect); 545 } 546 #endif 547 548 // See if there's a free slot 549 for (i = 0; i < haptic->neffects; i++) { 550 if (haptic->effects[i].hweffect == NULL) { 551 552 // Now let the backend create the real effect 553 if (!SDL_SYS_HapticNewEffect(haptic, &haptic->effects[i], effect)) { 554 return -1; // Backend failed to create effect 555 } 556 557 SDL_memcpy(&haptic->effects[i].effect, effect, 558 sizeof(SDL_HapticEffect)); 559 return i; 560 } 561 } 562 563 SDL_SetError("Haptic: Device has no free space left."); 564 return -1; 565} 566 567static bool ValidEffect(SDL_Haptic *haptic, SDL_HapticEffectID effect) 568{ 569 if ((effect < 0) || (effect >= haptic->neffects)) { 570 SDL_SetError("Haptic: Invalid effect identifier."); 571 return false; 572 } 573 return true; 574} 575 576bool SDL_UpdateHapticEffect(SDL_Haptic *haptic, SDL_HapticEffectID effect, const SDL_HapticEffect *data) 577{ 578 CHECK_HAPTIC_MAGIC(haptic, false); 579 580 CHECK_PARAM(!ValidEffect(haptic, effect)) { 581 return false; 582 } 583 584 CHECK_PARAM(!data) { 585 return SDL_InvalidParamError("data"); 586 } 587 588 // Can't change type dynamically. 589 CHECK_PARAM(data->type != haptic->effects[effect].effect.type) { 590 return SDL_SetError("Haptic: Updating effect type is illegal."); 591 } 592 593 #ifdef SDL_JOYSTICK_HIDAPI 594 if (SDL_HIDAPI_HapticIsHidapi(haptic)) { 595 return SDL_HIDAPI_HapticUpdateEffect(haptic, effect, data); 596 } 597 #endif 598 599 // Updates the effect 600 if (!SDL_SYS_HapticUpdateEffect(haptic, &haptic->effects[effect], data)) { 601 return false; 602 } 603 604 SDL_memcpy(&haptic->effects[effect].effect, data, 605 sizeof(SDL_HapticEffect)); 606 return true; 607} 608 609bool SDL_RunHapticEffect(SDL_Haptic *haptic, SDL_HapticEffectID effect, Uint32 iterations) 610{ 611 CHECK_HAPTIC_MAGIC(haptic, false); 612 613 #ifdef SDL_JOYSTICK_HIDAPI 614 if (SDL_HIDAPI_HapticIsHidapi(haptic)) { 615 return SDL_HIDAPI_HapticRunEffect(haptic, effect, iterations); 616 } 617 #endif 618 619 if (!ValidEffect(haptic, effect)) { 620 return false; 621 } 622 623 // Run the effect 624 if (!SDL_SYS_HapticRunEffect(haptic, &haptic->effects[effect], iterations)) { 625 return false; 626 } 627 628 return true; 629} 630 631bool SDL_StopHapticEffect(SDL_Haptic *haptic, SDL_HapticEffectID effect) 632{ 633 CHECK_HAPTIC_MAGIC(haptic, false); 634 635 #ifdef SDL_JOYSTICK_HIDAPI 636 if (SDL_HIDAPI_HapticIsHidapi(haptic)) { 637 return SDL_HIDAPI_HapticStopEffect(haptic, effect); 638 } 639 #endif 640 641 if (!ValidEffect(haptic, effect)) { 642 return false; 643 } 644 645 // Stop the effect 646 if (!SDL_SYS_HapticStopEffect(haptic, &haptic->effects[effect])) { 647 return false; 648 } 649 650 return true; 651} 652 653void SDL_DestroyHapticEffect(SDL_Haptic *haptic, SDL_HapticEffectID effect) 654{ 655 CHECK_HAPTIC_MAGIC(haptic,); 656 657 #ifdef SDL_JOYSTICK_HIDAPI 658 if (SDL_HIDAPI_HapticIsHidapi(haptic)) { 659 SDL_HIDAPI_HapticDestroyEffect(haptic, effect); 660 return; 661 } 662 #endif 663 664 if (!ValidEffect(haptic, effect)) { 665 return; 666 } 667 668 // Not allocated 669 if (haptic->effects[effect].hweffect == NULL) { 670 return; 671 } 672 673 SDL_SYS_HapticDestroyEffect(haptic, &haptic->effects[effect]); 674} 675 676bool SDL_GetHapticEffectStatus(SDL_Haptic *haptic, SDL_HapticEffectID effect) 677{ 678 CHECK_HAPTIC_MAGIC(haptic, false); 679 680 #ifdef SDL_JOYSTICK_HIDAPI 681 if (SDL_HIDAPI_HapticIsHidapi(haptic)) { 682 return SDL_HIDAPI_HapticGetEffectStatus(haptic, effect); 683 } 684 #endif 685 686 if (!ValidEffect(haptic, effect)) { 687 return false; 688 } 689 690 if (!(haptic->supported & SDL_HAPTIC_STATUS)) { 691 return SDL_SetError("Haptic: Device does not support status queries."); 692 } 693 694 SDL_ClearError(); 695 696 return (SDL_SYS_HapticGetEffectStatus(haptic, &haptic->effects[effect]) > 0); 697} 698 699bool SDL_SetHapticGain(SDL_Haptic *haptic, int gain) 700{ 701 const char *env; 702 int real_gain, max_gain; 703 704 CHECK_HAPTIC_MAGIC(haptic, false); 705 706 if (!(haptic->supported & SDL_HAPTIC_GAIN)) { 707 return SDL_SetError("Haptic: Device does not support setting gain."); 708 } 709 710 if ((gain < 0) || (gain > 100)) { 711 return SDL_SetError("Haptic: Gain must be between 0 and 100."); 712 } 713 714 // The user can use an environment variable to override the max gain. 715 env = SDL_getenv("SDL_HAPTIC_GAIN_MAX"); 716 if (env) { 717 max_gain = SDL_atoi(env); 718 719 // Check for sanity. 720 if (max_gain < 0) { 721 max_gain = 0; 722 } else if (max_gain > 100) { 723 max_gain = 100; 724 } 725 726 // We'll scale it linearly with SDL_HAPTIC_GAIN_MAX 727 real_gain = (gain * max_gain) / 100; 728 } else { 729 real_gain = gain; 730 } 731 732 #ifdef SDL_JOYSTICK_HIDAPI 733 if (SDL_HIDAPI_HapticIsHidapi(haptic)) { 734 return SDL_HIDAPI_HapticSetGain(haptic, real_gain); 735 } 736 #endif 737 738 return SDL_SYS_HapticSetGain(haptic, real_gain); 739} 740 741bool SDL_SetHapticAutocenter(SDL_Haptic *haptic, int autocenter) 742{ 743 CHECK_HAPTIC_MAGIC(haptic, false); 744 745 if (!(haptic->supported & SDL_HAPTIC_AUTOCENTER)) { 746 return SDL_SetError("Haptic: Device does not support setting autocenter."); 747 } 748 749 if ((autocenter < 0) || (autocenter > 100)) { 750 return SDL_SetError("Haptic: Autocenter must be between 0 and 100."); 751 } 752 753 #ifdef SDL_JOYSTICK_HIDAPI 754 if (SDL_HIDAPI_HapticIsHidapi(haptic)) { 755 return SDL_HIDAPI_HapticSetAutocenter(haptic, autocenter); 756 } 757 #endif 758 759 return SDL_SYS_HapticSetAutocenter(haptic, autocenter); 760} 761 762bool SDL_PauseHaptic(SDL_Haptic *haptic) 763{ 764 CHECK_HAPTIC_MAGIC(haptic, false); 765 766 if (!(haptic->supported & SDL_HAPTIC_PAUSE)) { 767 return SDL_SetError("Haptic: Device does not support setting pausing."); 768 } 769 770 #ifdef SDL_JOYSTICK_HIDAPI 771 if (SDL_HIDAPI_HapticIsHidapi(haptic)) { 772 return SDL_HIDAPI_HapticPause(haptic); 773 } 774 #endif 775 776 return SDL_SYS_HapticPause(haptic); 777} 778 779bool SDL_ResumeHaptic(SDL_Haptic *haptic) 780{ 781 CHECK_HAPTIC_MAGIC(haptic, false); 782 783 if (!(haptic->supported & SDL_HAPTIC_PAUSE)) { 784 return true; // Not going to be paused, so we pretend it's unpaused. 785 } 786 787 #ifdef SDL_JOYSTICK_HIDAPI 788 if (SDL_HIDAPI_HapticIsHidapi(haptic)) { 789 return SDL_HIDAPI_HapticResume(haptic); 790 } 791 #endif 792 793 return SDL_SYS_HapticResume(haptic); 794} 795 796bool SDL_StopHapticEffects(SDL_Haptic *haptic) 797{ 798 CHECK_HAPTIC_MAGIC(haptic, false); 799 800 #ifdef SDL_JOYSTICK_HIDAPI 801 if (SDL_HIDAPI_HapticIsHidapi(haptic)) { 802 return SDL_HIDAPI_HapticStopAll(haptic); 803 } 804 #endif 805 806 return SDL_SYS_HapticStopAll(haptic); 807} 808 809bool SDL_HapticRumbleSupported(SDL_Haptic *haptic) 810{ 811 CHECK_HAPTIC_MAGIC(haptic, false); 812 813 // Most things can use SINE, but XInput only has LEFTRIGHT. 814 return (haptic->supported & (SDL_HAPTIC_SINE | SDL_HAPTIC_LEFTRIGHT)) != 0; 815} 816 817bool SDL_InitHapticRumble(SDL_Haptic *haptic) 818{ 819 SDL_HapticEffect *efx = &haptic->rumble_effect; 820 821 CHECK_HAPTIC_MAGIC(haptic, false); 822 823 // Already allocated. 824 if (haptic->rumble_id >= 0) { 825 return true; 826 } 827 828 SDL_zerop(efx); 829 if (haptic->supported & SDL_HAPTIC_SINE) { 830 efx->type = SDL_HAPTIC_SINE; 831 efx->periodic.direction.type = SDL_HAPTIC_CARTESIAN; 832 efx->periodic.period = 1000; 833 efx->periodic.magnitude = 0x4000; 834 efx->periodic.length = 5000; 835 efx->periodic.attack_length = 0; 836 efx->periodic.fade_length = 0; 837 } else if (haptic->supported & SDL_HAPTIC_LEFTRIGHT) { // XInput? 838 efx->type = SDL_HAPTIC_LEFTRIGHT; 839 efx->leftright.length = 5000; 840 efx->leftright.large_magnitude = 0x4000; 841 efx->leftright.small_magnitude = 0x4000; 842 } else { 843 return SDL_SetError("Device doesn't support rumble"); 844 } 845 846 haptic->rumble_id = SDL_CreateHapticEffect(haptic, &haptic->rumble_effect); 847 if (haptic->rumble_id >= 0) { 848 return true; 849 } 850 return false; 851} 852 853bool SDL_PlayHapticRumble(SDL_Haptic *haptic, float strength, Uint32 length) 854{ 855 SDL_HapticEffect *efx; 856 Sint16 magnitude; 857 858 CHECK_HAPTIC_MAGIC(haptic, false); 859 860 if (haptic->rumble_id < 0) { 861 return SDL_SetError("Haptic: Rumble effect not initialized on haptic device"); 862 } 863 864 // Clamp strength. 865 if (strength > 1.0f) { 866 strength = 1.0f; 867 } else if (strength < 0.0f) { 868 strength = 0.0f; 869 } 870 magnitude = (Sint16)(32767.0f * strength); 871 872 efx = &haptic->rumble_effect; 873 if (efx->type == SDL_HAPTIC_SINE) { 874 efx->periodic.magnitude = magnitude; 875 efx->periodic.length = length; 876 } else if (efx->type == SDL_HAPTIC_LEFTRIGHT) { 877 efx->leftright.small_magnitude = efx->leftright.large_magnitude = magnitude; 878 efx->leftright.length = length; 879 } else { 880 SDL_assert(!"This should have been caught elsewhere"); 881 } 882 883 if (!SDL_UpdateHapticEffect(haptic, haptic->rumble_id, &haptic->rumble_effect)) { 884 return false; 885 } 886 887 return SDL_RunHapticEffect(haptic, haptic->rumble_id, 1); 888} 889 890bool SDL_StopHapticRumble(SDL_Haptic *haptic) 891{ 892 CHECK_HAPTIC_MAGIC(haptic, false); 893 894 if (haptic->rumble_id < 0) { 895 return SDL_SetError("Haptic: Rumble effect not initialized on haptic device"); 896 } 897 898 return SDL_StopHapticEffect(haptic, haptic->rumble_id); 899} 900[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.