SDL_haptic.c (18958B)
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 #include "../SDL_internal.h" 22 23 #include "SDL_syshaptic.h" 24 #include "SDL_haptic_c.h" 25 #include "../joystick/SDL_joystick_c.h" /* For SDL_PrivateJoystickValid */ 26 27 /* Global for SDL_windowshaptic.c */ 28 #if (defined(SDL_HAPTIC_DINPUT) && SDL_HAPTIC_DINPUT) || (defined(SDL_HAPTIC_XINPUT) && SDL_HAPTIC_XINPUT) 29 SDL_Haptic *SDL_haptics = NULL; 30 #else 31 static SDL_Haptic *SDL_haptics = NULL; 32 #endif 33 34 /* 35 * Initializes the Haptic devices. 36 */ 37 int 38 SDL_HapticInit(void) 39 { 40 int status; 41 42 status = SDL_SYS_HapticInit(); 43 if (status >= 0) { 44 status = 0; 45 } 46 47 return status; 48 } 49 50 51 /* 52 * Checks to see if the haptic device is valid 53 */ 54 static int 55 ValidHaptic(SDL_Haptic * haptic) 56 { 57 int valid; 58 SDL_Haptic *hapticlist; 59 60 valid = 0; 61 if (haptic != NULL) { 62 hapticlist = SDL_haptics; 63 while ( hapticlist ) 64 { 65 if (hapticlist == haptic) { 66 valid = 1; 67 break; 68 } 69 hapticlist = hapticlist->next; 70 } 71 } 72 73 /* Create the error here. */ 74 if (valid == 0) { 75 SDL_SetError("Haptic: Invalid haptic device identifier"); 76 } 77 78 return valid; 79 } 80 81 82 /* 83 * Returns the number of available devices. 84 */ 85 int 86 SDL_NumHaptics(void) 87 { 88 return SDL_SYS_NumHaptics(); 89 } 90 91 92 /* 93 * Gets the name of a Haptic device by index. 94 */ 95 const char * 96 SDL_HapticName(int device_index) 97 { 98 if ((device_index < 0) || (device_index >= SDL_NumHaptics())) { 99 SDL_SetError("Haptic: There are %d haptic devices available", 100 SDL_NumHaptics()); 101 return NULL; 102 } 103 return SDL_SYS_HapticName(device_index); 104 } 105 106 107 /* 108 * Opens a Haptic device. 109 */ 110 SDL_Haptic * 111 SDL_HapticOpen(int device_index) 112 { 113 SDL_Haptic *haptic; 114 SDL_Haptic *hapticlist; 115 116 if ((device_index < 0) || (device_index >= SDL_NumHaptics())) { 117 SDL_SetError("Haptic: There are %d haptic devices available", 118 SDL_NumHaptics()); 119 return NULL; 120 } 121 122 hapticlist = SDL_haptics; 123 /* If the haptic is already open, return it 124 * TODO: Should we create haptic instance IDs like the Joystick API? 125 */ 126 while ( hapticlist ) 127 { 128 if (device_index == hapticlist->index) { 129 haptic = hapticlist; 130 ++haptic->ref_count; 131 return haptic; 132 } 133 hapticlist = hapticlist->next; 134 } 135 136 /* Create the haptic device */ 137 haptic = (SDL_Haptic *) SDL_malloc((sizeof *haptic)); 138 if (haptic == NULL) { 139 SDL_OutOfMemory(); 140 return NULL; 141 } 142 143 /* Initialize the haptic device */ 144 SDL_memset(haptic, 0, (sizeof *haptic)); 145 haptic->rumble_id = -1; 146 haptic->index = device_index; 147 if (SDL_SYS_HapticOpen(haptic) < 0) { 148 SDL_free(haptic); 149 return NULL; 150 } 151 152 /* Add haptic to list */ 153 ++haptic->ref_count; 154 /* Link the haptic in the list */ 155 haptic->next = SDL_haptics; 156 SDL_haptics = haptic; 157 158 /* Disable autocenter and set gain to max. */ 159 if (haptic->supported & SDL_HAPTIC_GAIN) 160 SDL_HapticSetGain(haptic, 100); 161 if (haptic->supported & SDL_HAPTIC_AUTOCENTER) 162 SDL_HapticSetAutocenter(haptic, 0); 163 164 return haptic; 165 } 166 167 168 /* 169 * Returns 1 if the device has been opened. 170 */ 171 int 172 SDL_HapticOpened(int device_index) 173 { 174 int opened; 175 SDL_Haptic *hapticlist; 176 177 /* Make sure it's valid. */ 178 if ((device_index < 0) || (device_index >= SDL_NumHaptics())) { 179 SDL_SetError("Haptic: There are %d haptic devices available", 180 SDL_NumHaptics()); 181 return 0; 182 } 183 184 opened = 0; 185 hapticlist = SDL_haptics; 186 /* TODO Should this use an instance ID? */ 187 while ( hapticlist ) 188 { 189 if (hapticlist->index == (Uint8) device_index) { 190 opened = 1; 191 break; 192 } 193 hapticlist = hapticlist->next; 194 } 195 return opened; 196 } 197 198 199 /* 200 * Returns the index to a haptic device. 201 */ 202 int 203 SDL_HapticIndex(SDL_Haptic * haptic) 204 { 205 if (!ValidHaptic(haptic)) { 206 return -1; 207 } 208 209 return haptic->index; 210 } 211 212 213 /* 214 * Returns SDL_TRUE if mouse is haptic, SDL_FALSE if it isn't. 215 */ 216 int 217 SDL_MouseIsHaptic(void) 218 { 219 if (SDL_SYS_HapticMouse() < 0) 220 return SDL_FALSE; 221 return SDL_TRUE; 222 } 223 224 225 /* 226 * Returns the haptic device if mouse is haptic or NULL elsewise. 227 */ 228 SDL_Haptic * 229 SDL_HapticOpenFromMouse(void) 230 { 231 int device_index; 232 233 device_index = SDL_SYS_HapticMouse(); 234 235 if (device_index < 0) { 236 SDL_SetError("Haptic: Mouse isn't a haptic device."); 237 return NULL; 238 } 239 240 return SDL_HapticOpen(device_index); 241 } 242 243 244 /* 245 * Returns SDL_TRUE if joystick has haptic features. 246 */ 247 int 248 SDL_JoystickIsHaptic(SDL_Joystick * joystick) 249 { 250 int ret; 251 252 /* Must be a valid joystick */ 253 if (!SDL_PrivateJoystickValid(joystick)) { 254 return -1; 255 } 256 257 ret = SDL_SYS_JoystickIsHaptic(joystick); 258 259 if (ret > 0) 260 return SDL_TRUE; 261 else if (ret == 0) 262 return SDL_FALSE; 263 else 264 return -1; 265 } 266 267 268 /* 269 * Opens a haptic device from a joystick. 270 */ 271 SDL_Haptic * 272 SDL_HapticOpenFromJoystick(SDL_Joystick * joystick) 273 { 274 SDL_Haptic *haptic; 275 SDL_Haptic *hapticlist; 276 277 /* Make sure there is room. */ 278 if (SDL_NumHaptics() <= 0) { 279 SDL_SetError("Haptic: There are %d haptic devices available", 280 SDL_NumHaptics()); 281 return NULL; 282 } 283 284 /* Must be a valid joystick */ 285 if (!SDL_PrivateJoystickValid(joystick)) { 286 SDL_SetError("Haptic: Joystick isn't valid."); 287 return NULL; 288 } 289 290 /* Joystick must be haptic */ 291 if (SDL_SYS_JoystickIsHaptic(joystick) <= 0) { 292 SDL_SetError("Haptic: Joystick isn't a haptic device."); 293 return NULL; 294 } 295 296 hapticlist = SDL_haptics; 297 /* Check to see if joystick's haptic is already open */ 298 while ( hapticlist ) 299 { 300 if (SDL_SYS_JoystickSameHaptic(hapticlist, joystick)) { 301 haptic = hapticlist; 302 ++haptic->ref_count; 303 return haptic; 304 } 305 hapticlist = hapticlist->next; 306 } 307 308 /* Create the haptic device */ 309 haptic = (SDL_Haptic *) SDL_malloc((sizeof *haptic)); 310 if (haptic == NULL) { 311 SDL_OutOfMemory(); 312 return NULL; 313 } 314 315 /* Initialize the haptic device */ 316 SDL_memset(haptic, 0, sizeof(SDL_Haptic)); 317 haptic->rumble_id = -1; 318 if (SDL_SYS_HapticOpenFromJoystick(haptic, joystick) < 0) { 319 SDL_SetError("Haptic: SDL_SYS_HapticOpenFromJoystick failed."); 320 SDL_free(haptic); 321 return NULL; 322 } 323 324 /* Add haptic to list */ 325 ++haptic->ref_count; 326 /* Link the haptic in the list */ 327 haptic->next = SDL_haptics; 328 SDL_haptics = haptic; 329 330 return haptic; 331 } 332 333 334 /* 335 * Closes a SDL_Haptic device. 336 */ 337 void 338 SDL_HapticClose(SDL_Haptic * haptic) 339 { 340 int i; 341 SDL_Haptic *hapticlist; 342 SDL_Haptic *hapticlistprev; 343 344 /* Must be valid */ 345 if (!ValidHaptic(haptic)) { 346 return; 347 } 348 349 /* Check if it's still in use */ 350 if (--haptic->ref_count > 0) { 351 return; 352 } 353 354 /* Close it, properly removing effects if needed */ 355 for (i = 0; i < haptic->neffects; i++) { 356 if (haptic->effects[i].hweffect != NULL) { 357 SDL_HapticDestroyEffect(haptic, i); 358 } 359 } 360 SDL_SYS_HapticClose(haptic); 361 362 /* Remove from the list */ 363 hapticlist = SDL_haptics; 364 hapticlistprev = NULL; 365 while ( hapticlist ) 366 { 367 if (haptic == hapticlist) 368 { 369 if ( hapticlistprev ) 370 { 371 /* unlink this entry */ 372 hapticlistprev->next = hapticlist->next; 373 } 374 else 375 { 376 SDL_haptics = haptic->next; 377 } 378 379 break; 380 } 381 hapticlistprev = hapticlist; 382 hapticlist = hapticlist->next; 383 } 384 385 /* Free */ 386 SDL_free(haptic); 387 } 388 389 /* 390 * Cleans up after the subsystem. 391 */ 392 void 393 SDL_HapticQuit(void) 394 { 395 while (SDL_haptics) { 396 SDL_HapticClose(SDL_haptics); 397 } 398 399 SDL_SYS_HapticQuit(); 400 } 401 402 /* 403 * Returns the number of effects a haptic device has. 404 */ 405 int 406 SDL_HapticNumEffects(SDL_Haptic * haptic) 407 { 408 if (!ValidHaptic(haptic)) { 409 return -1; 410 } 411 412 return haptic->neffects; 413 } 414 415 416 /* 417 * Returns the number of effects a haptic device can play. 418 */ 419 int 420 SDL_HapticNumEffectsPlaying(SDL_Haptic * haptic) 421 { 422 if (!ValidHaptic(haptic)) { 423 return -1; 424 } 425 426 return haptic->nplaying; 427 } 428 429 430 /* 431 * Returns supported effects by the device. 432 */ 433 unsigned int 434 SDL_HapticQuery(SDL_Haptic * haptic) 435 { 436 if (!ValidHaptic(haptic)) { 437 return 0; /* same as if no effects were supported */ 438 } 439 440 return haptic->supported; 441 } 442 443 444 /* 445 * Returns the number of axis on the device. 446 */ 447 int 448 SDL_HapticNumAxes(SDL_Haptic * haptic) 449 { 450 if (!ValidHaptic(haptic)) { 451 return -1; 452 } 453 454 return haptic->naxes; 455 } 456 457 /* 458 * Checks to see if the device can support the effect. 459 */ 460 int 461 SDL_HapticEffectSupported(SDL_Haptic * haptic, SDL_HapticEffect * effect) 462 { 463 if (!ValidHaptic(haptic)) { 464 return -1; 465 } 466 467 if ((haptic->supported & effect->type) != 0) 468 return SDL_TRUE; 469 return SDL_FALSE; 470 } 471 472 /* 473 * Creates a new haptic effect. 474 */ 475 int 476 SDL_HapticNewEffect(SDL_Haptic * haptic, SDL_HapticEffect * effect) 477 { 478 int i; 479 480 /* Check for device validity. */ 481 if (!ValidHaptic(haptic)) { 482 return -1; 483 } 484 485 /* Check to see if effect is supported */ 486 if (SDL_HapticEffectSupported(haptic, effect) == SDL_FALSE) { 487 return SDL_SetError("Haptic: Effect not supported by haptic device."); 488 } 489 490 /* See if there's a free slot */ 491 for (i = 0; i < haptic->neffects; i++) { 492 if (haptic->effects[i].hweffect == NULL) { 493 494 /* Now let the backend create the real effect */ 495 if (SDL_SYS_HapticNewEffect(haptic, &haptic->effects[i], effect) 496 != 0) { 497 return -1; /* Backend failed to create effect */ 498 } 499 500 SDL_memcpy(&haptic->effects[i].effect, effect, 501 sizeof(SDL_HapticEffect)); 502 return i; 503 } 504 } 505 506 return SDL_SetError("Haptic: Device has no free space left."); 507 } 508 509 /* 510 * Checks to see if an effect is valid. 511 */ 512 static int 513 ValidEffect(SDL_Haptic * haptic, int effect) 514 { 515 if ((effect < 0) || (effect >= haptic->neffects)) { 516 SDL_SetError("Haptic: Invalid effect identifier."); 517 return 0; 518 } 519 return 1; 520 } 521 522 /* 523 * Updates an effect. 524 */ 525 int 526 SDL_HapticUpdateEffect(SDL_Haptic * haptic, int effect, 527 SDL_HapticEffect * data) 528 { 529 if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { 530 return -1; 531 } 532 533 /* Can't change type dynamically. */ 534 if (data->type != haptic->effects[effect].effect.type) { 535 return SDL_SetError("Haptic: Updating effect type is illegal."); 536 } 537 538 /* Updates the effect */ 539 if (SDL_SYS_HapticUpdateEffect(haptic, &haptic->effects[effect], data) < 540 0) { 541 return -1; 542 } 543 544 SDL_memcpy(&haptic->effects[effect].effect, data, 545 sizeof(SDL_HapticEffect)); 546 return 0; 547 } 548 549 550 /* 551 * Runs the haptic effect on the device. 552 */ 553 int 554 SDL_HapticRunEffect(SDL_Haptic * haptic, int effect, Uint32 iterations) 555 { 556 if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { 557 return -1; 558 } 559 560 /* Run the effect */ 561 if (SDL_SYS_HapticRunEffect(haptic, &haptic->effects[effect], iterations) 562 < 0) { 563 return -1; 564 } 565 566 return 0; 567 } 568 569 /* 570 * Stops the haptic effect on the device. 571 */ 572 int 573 SDL_HapticStopEffect(SDL_Haptic * haptic, int effect) 574 { 575 if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { 576 return -1; 577 } 578 579 /* Stop the effect */ 580 if (SDL_SYS_HapticStopEffect(haptic, &haptic->effects[effect]) < 0) { 581 return -1; 582 } 583 584 return 0; 585 } 586 587 /* 588 * Gets rid of a haptic effect. 589 */ 590 void 591 SDL_HapticDestroyEffect(SDL_Haptic * haptic, int effect) 592 { 593 if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { 594 return; 595 } 596 597 /* Not allocated */ 598 if (haptic->effects[effect].hweffect == NULL) { 599 return; 600 } 601 602 SDL_SYS_HapticDestroyEffect(haptic, &haptic->effects[effect]); 603 } 604 605 /* 606 * Gets the status of a haptic effect. 607 */ 608 int 609 SDL_HapticGetEffectStatus(SDL_Haptic * haptic, int effect) 610 { 611 if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { 612 return -1; 613 } 614 615 if ((haptic->supported & SDL_HAPTIC_STATUS) == 0) { 616 return SDL_SetError("Haptic: Device does not support status queries."); 617 } 618 619 return SDL_SYS_HapticGetEffectStatus(haptic, &haptic->effects[effect]); 620 } 621 622 /* 623 * Sets the global gain of the device. 624 */ 625 int 626 SDL_HapticSetGain(SDL_Haptic * haptic, int gain) 627 { 628 const char *env; 629 int real_gain, max_gain; 630 631 if (!ValidHaptic(haptic)) { 632 return -1; 633 } 634 635 if ((haptic->supported & SDL_HAPTIC_GAIN) == 0) { 636 return SDL_SetError("Haptic: Device does not support setting gain."); 637 } 638 639 if ((gain < 0) || (gain > 100)) { 640 return SDL_SetError("Haptic: Gain must be between 0 and 100."); 641 } 642 643 /* We use the envvar to get the maximum gain. */ 644 env = SDL_getenv("SDL_HAPTIC_GAIN_MAX"); 645 if (env != NULL) { 646 max_gain = SDL_atoi(env); 647 648 /* Check for sanity. */ 649 if (max_gain < 0) 650 max_gain = 0; 651 else if (max_gain > 100) 652 max_gain = 100; 653 654 /* We'll scale it linearly with SDL_HAPTIC_GAIN_MAX */ 655 real_gain = (gain * max_gain) / 100; 656 } else { 657 real_gain = gain; 658 } 659 660 if (SDL_SYS_HapticSetGain(haptic, real_gain) < 0) { 661 return -1; 662 } 663 664 return 0; 665 } 666 667 /* 668 * Makes the device autocenter, 0 disables. 669 */ 670 int 671 SDL_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) 672 { 673 if (!ValidHaptic(haptic)) { 674 return -1; 675 } 676 677 if ((haptic->supported & SDL_HAPTIC_AUTOCENTER) == 0) { 678 return SDL_SetError("Haptic: Device does not support setting autocenter."); 679 } 680 681 if ((autocenter < 0) || (autocenter > 100)) { 682 return SDL_SetError("Haptic: Autocenter must be between 0 and 100."); 683 } 684 685 if (SDL_SYS_HapticSetAutocenter(haptic, autocenter) < 0) { 686 return -1; 687 } 688 689 return 0; 690 } 691 692 /* 693 * Pauses the haptic device. 694 */ 695 int 696 SDL_HapticPause(SDL_Haptic * haptic) 697 { 698 if (!ValidHaptic(haptic)) { 699 return -1; 700 } 701 702 if ((haptic->supported & SDL_HAPTIC_PAUSE) == 0) { 703 return SDL_SetError("Haptic: Device does not support setting pausing."); 704 } 705 706 return SDL_SYS_HapticPause(haptic); 707 } 708 709 /* 710 * Unpauses the haptic device. 711 */ 712 int 713 SDL_HapticUnpause(SDL_Haptic * haptic) 714 { 715 if (!ValidHaptic(haptic)) { 716 return -1; 717 } 718 719 if ((haptic->supported & SDL_HAPTIC_PAUSE) == 0) { 720 return 0; /* Not going to be paused, so we pretend it's unpaused. */ 721 } 722 723 return SDL_SYS_HapticUnpause(haptic); 724 } 725 726 /* 727 * Stops all the currently playing effects. 728 */ 729 int 730 SDL_HapticStopAll(SDL_Haptic * haptic) 731 { 732 if (!ValidHaptic(haptic)) { 733 return -1; 734 } 735 736 return SDL_SYS_HapticStopAll(haptic); 737 } 738 739 /* 740 * Checks to see if rumble is supported. 741 */ 742 int 743 SDL_HapticRumbleSupported(SDL_Haptic * haptic) 744 { 745 if (!ValidHaptic(haptic)) { 746 return -1; 747 } 748 749 /* Most things can use SINE, but XInput only has LEFTRIGHT. */ 750 return ((haptic->supported & (SDL_HAPTIC_SINE|SDL_HAPTIC_LEFTRIGHT)) != 0); 751 } 752 753 /* 754 * Initializes the haptic device for simple rumble playback. 755 */ 756 int 757 SDL_HapticRumbleInit(SDL_Haptic * haptic) 758 { 759 SDL_HapticEffect *efx = &haptic->rumble_effect; 760 761 if (!ValidHaptic(haptic)) { 762 return -1; 763 } 764 765 /* Already allocated. */ 766 if (haptic->rumble_id >= 0) { 767 return 0; 768 } 769 770 SDL_zerop(efx); 771 if (haptic->supported & SDL_HAPTIC_SINE) { 772 efx->type = SDL_HAPTIC_SINE; 773 efx->periodic.direction.type = SDL_HAPTIC_CARTESIAN; 774 efx->periodic.period = 1000; 775 efx->periodic.magnitude = 0x4000; 776 efx->periodic.length = 5000; 777 efx->periodic.attack_length = 0; 778 efx->periodic.fade_length = 0; 779 } else if (haptic->supported & SDL_HAPTIC_LEFTRIGHT) { /* XInput? */ 780 efx->type = SDL_HAPTIC_LEFTRIGHT; 781 efx->leftright.length = 5000; 782 efx->leftright.large_magnitude = 0x4000; 783 efx->leftright.small_magnitude = 0x4000; 784 } else { 785 return SDL_SetError("Device doesn't support rumble"); 786 } 787 788 haptic->rumble_id = SDL_HapticNewEffect(haptic, &haptic->rumble_effect); 789 if (haptic->rumble_id >= 0) { 790 return 0; 791 } 792 return -1; 793 } 794 795 /* 796 * Runs simple rumble on a haptic device 797 */ 798 int 799 SDL_HapticRumblePlay(SDL_Haptic * haptic, float strength, Uint32 length) 800 { 801 SDL_HapticEffect *efx; 802 Sint16 magnitude; 803 804 if (!ValidHaptic(haptic)) { 805 return -1; 806 } 807 808 if (haptic->rumble_id < 0) { 809 return SDL_SetError("Haptic: Rumble effect not initialized on haptic device"); 810 } 811 812 /* Clamp strength. */ 813 if (strength > 1.0f) { 814 strength = 1.0f; 815 } else if (strength < 0.0f) { 816 strength = 0.0f; 817 } 818 magnitude = (Sint16)(32767.0f*strength); 819 820 efx = &haptic->rumble_effect; 821 if (efx->type == SDL_HAPTIC_SINE) { 822 efx->periodic.magnitude = magnitude; 823 efx->periodic.length = length; 824 } else if (efx->type == SDL_HAPTIC_LEFTRIGHT) { 825 efx->leftright.small_magnitude = efx->leftright.large_magnitude = magnitude; 826 efx->leftright.length = length; 827 } else { 828 SDL_assert(0 && "This should have been caught elsewhere"); 829 } 830 831 if (SDL_HapticUpdateEffect(haptic, haptic->rumble_id, &haptic->rumble_effect) < 0) { 832 return -1; 833 } 834 835 return SDL_HapticRunEffect(haptic, haptic->rumble_id, 1); 836 } 837 838 /* 839 * Stops the simple rumble on a haptic device. 840 */ 841 int 842 SDL_HapticRumbleStop(SDL_Haptic * haptic) 843 { 844 if (!ValidHaptic(haptic)) { 845 return -1; 846 } 847 848 if (haptic->rumble_id < 0) { 849 return SDL_SetError("Haptic: Rumble effect not initialized on haptic device"); 850 } 851 852 return SDL_HapticStopEffect(haptic, haptic->rumble_id); 853 } 854 855 /* vi: set ts=4 sw=4 expandtab: */