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