Atlas - SDL_haptic.c

Home / ext / SDL2 / src / haptic Lines: 1 | Size: 18812 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 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#include "SDL_haptic_c.h" 25#include "../joystick/SDL_joystick_c.h" /* For SDL_PrivateJoystickValid */ 26#include "SDL_assert.h" 27 28/* Global for SDL_windowshaptic.c */ 29SDL_Haptic *SDL_haptics = NULL; 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_SetError("Haptic: SDL_SYS_HapticOpenFromJoystick failed."); 317 SDL_free(haptic); 318 return NULL; 319 } 320 321 /* Add haptic to list */ 322 ++haptic->ref_count; 323 /* Link the haptic in the list */ 324 haptic->next = SDL_haptics; 325 SDL_haptics = haptic; 326 327 return haptic; 328} 329 330 331/* 332 * Closes a SDL_Haptic device. 333 */ 334void 335SDL_HapticClose(SDL_Haptic * haptic) 336{ 337 int i; 338 SDL_Haptic *hapticlist; 339 SDL_Haptic *hapticlistprev; 340 341 /* Must be valid */ 342 if (!ValidHaptic(haptic)) { 343 return; 344 } 345 346 /* Check if it's still in use */ 347 if (--haptic->ref_count > 0) { 348 return; 349 } 350 351 /* Close it, properly removing effects if needed */ 352 for (i = 0; i < haptic->neffects; i++) { 353 if (haptic->effects[i].hweffect != NULL) { 354 SDL_HapticDestroyEffect(haptic, i); 355 } 356 } 357 SDL_SYS_HapticClose(haptic); 358 359 /* Remove from the list */ 360 hapticlist = SDL_haptics; 361 hapticlistprev = NULL; 362 while ( hapticlist ) 363 { 364 if (haptic == hapticlist) 365 { 366 if ( hapticlistprev ) 367 { 368 /* unlink this entry */ 369 hapticlistprev->next = hapticlist->next; 370 } 371 else 372 { 373 SDL_haptics = haptic->next; 374 } 375 376 break; 377 } 378 hapticlistprev = hapticlist; 379 hapticlist = hapticlist->next; 380 } 381 382 /* Free */ 383 SDL_free(haptic); 384} 385 386/* 387 * Cleans up after the subsystem. 388 */ 389void 390SDL_HapticQuit(void) 391{ 392 SDL_SYS_HapticQuit(); 393 SDL_assert(SDL_haptics == NULL); 394 SDL_haptics = NULL; 395} 396 397/* 398 * Returns the number of effects a haptic device has. 399 */ 400int 401SDL_HapticNumEffects(SDL_Haptic * haptic) 402{ 403 if (!ValidHaptic(haptic)) { 404 return -1; 405 } 406 407 return haptic->neffects; 408} 409 410 411/* 412 * Returns the number of effects a haptic device can play. 413 */ 414int 415SDL_HapticNumEffectsPlaying(SDL_Haptic * haptic) 416{ 417 if (!ValidHaptic(haptic)) { 418 return -1; 419 } 420 421 return haptic->nplaying; 422} 423 424 425/* 426 * Returns supported effects by the device. 427 */ 428unsigned int 429SDL_HapticQuery(SDL_Haptic * haptic) 430{ 431 if (!ValidHaptic(haptic)) { 432 return 0; /* same as if no effects were supported */ 433 } 434 435 return haptic->supported; 436} 437 438 439/* 440 * Returns the number of axis on the device. 441 */ 442int 443SDL_HapticNumAxes(SDL_Haptic * haptic) 444{ 445 if (!ValidHaptic(haptic)) { 446 return -1; 447 } 448 449 return haptic->naxes; 450} 451 452/* 453 * Checks to see if the device can support the effect. 454 */ 455int 456SDL_HapticEffectSupported(SDL_Haptic * haptic, SDL_HapticEffect * effect) 457{ 458 if (!ValidHaptic(haptic)) { 459 return -1; 460 } 461 462 if ((haptic->supported & effect->type) != 0) 463 return SDL_TRUE; 464 return SDL_FALSE; 465} 466 467/* 468 * Creates a new haptic effect. 469 */ 470int 471SDL_HapticNewEffect(SDL_Haptic * haptic, SDL_HapticEffect * effect) 472{ 473 int i; 474 475 /* Check for device validity. */ 476 if (!ValidHaptic(haptic)) { 477 return -1; 478 } 479 480 /* Check to see if effect is supported */ 481 if (SDL_HapticEffectSupported(haptic, effect) == SDL_FALSE) { 482 return SDL_SetError("Haptic: Effect not supported by haptic device."); 483 } 484 485 /* See if there's a free slot */ 486 for (i = 0; i < haptic->neffects; i++) { 487 if (haptic->effects[i].hweffect == NULL) { 488 489 /* Now let the backend create the real effect */ 490 if (SDL_SYS_HapticNewEffect(haptic, &haptic->effects[i], effect) 491 != 0) { 492 return -1; /* Backend failed to create effect */ 493 } 494 495 SDL_memcpy(&haptic->effects[i].effect, effect, 496 sizeof(SDL_HapticEffect)); 497 return i; 498 } 499 } 500 501 return SDL_SetError("Haptic: Device has no free space left."); 502} 503 504/* 505 * Checks to see if an effect is valid. 506 */ 507static int 508ValidEffect(SDL_Haptic * haptic, int effect) 509{ 510 if ((effect < 0) || (effect >= haptic->neffects)) { 511 SDL_SetError("Haptic: Invalid effect identifier."); 512 return 0; 513 } 514 return 1; 515} 516 517/* 518 * Updates an effect. 519 */ 520int 521SDL_HapticUpdateEffect(SDL_Haptic * haptic, int effect, 522 SDL_HapticEffect * data) 523{ 524 if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { 525 return -1; 526 } 527 528 /* Can't change type dynamically. */ 529 if (data->type != haptic->effects[effect].effect.type) { 530 return SDL_SetError("Haptic: Updating effect type is illegal."); 531 } 532 533 /* Updates the effect */ 534 if (SDL_SYS_HapticUpdateEffect(haptic, &haptic->effects[effect], data) < 535 0) { 536 return -1; 537 } 538 539 SDL_memcpy(&haptic->effects[effect].effect, data, 540 sizeof(SDL_HapticEffect)); 541 return 0; 542} 543 544 545/* 546 * Runs the haptic effect on the device. 547 */ 548int 549SDL_HapticRunEffect(SDL_Haptic * haptic, int effect, Uint32 iterations) 550{ 551 if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { 552 return -1; 553 } 554 555 /* Run the effect */ 556 if (SDL_SYS_HapticRunEffect(haptic, &haptic->effects[effect], iterations) 557 < 0) { 558 return -1; 559 } 560 561 return 0; 562} 563 564/* 565 * Stops the haptic effect on the device. 566 */ 567int 568SDL_HapticStopEffect(SDL_Haptic * haptic, int effect) 569{ 570 if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { 571 return -1; 572 } 573 574 /* Stop the effect */ 575 if (SDL_SYS_HapticStopEffect(haptic, &haptic->effects[effect]) < 0) { 576 return -1; 577 } 578 579 return 0; 580} 581 582/* 583 * Gets rid of a haptic effect. 584 */ 585void 586SDL_HapticDestroyEffect(SDL_Haptic * haptic, int effect) 587{ 588 if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { 589 return; 590 } 591 592 /* Not allocated */ 593 if (haptic->effects[effect].hweffect == NULL) { 594 return; 595 } 596 597 SDL_SYS_HapticDestroyEffect(haptic, &haptic->effects[effect]); 598} 599 600/* 601 * Gets the status of a haptic effect. 602 */ 603int 604SDL_HapticGetEffectStatus(SDL_Haptic * haptic, int effect) 605{ 606 if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { 607 return -1; 608 } 609 610 if ((haptic->supported & SDL_HAPTIC_STATUS) == 0) { 611 return SDL_SetError("Haptic: Device does not support status queries."); 612 } 613 614 return SDL_SYS_HapticGetEffectStatus(haptic, &haptic->effects[effect]); 615} 616 617/* 618 * Sets the global gain of the device. 619 */ 620int 621SDL_HapticSetGain(SDL_Haptic * haptic, int gain) 622{ 623 const char *env; 624 int real_gain, max_gain; 625 626 if (!ValidHaptic(haptic)) { 627 return -1; 628 } 629 630 if ((haptic->supported & SDL_HAPTIC_GAIN) == 0) { 631 return SDL_SetError("Haptic: Device does not support setting gain."); 632 } 633 634 if ((gain < 0) || (gain > 100)) { 635 return SDL_SetError("Haptic: Gain must be between 0 and 100."); 636 } 637 638 /* We use the envvar to get the maximum gain. */ 639 env = SDL_getenv("SDL_HAPTIC_GAIN_MAX"); 640 if (env != NULL) { 641 max_gain = SDL_atoi(env); 642 643 /* Check for sanity. */ 644 if (max_gain < 0) 645 max_gain = 0; 646 else if (max_gain > 100) 647 max_gain = 100; 648 649 /* We'll scale it linearly with SDL_HAPTIC_GAIN_MAX */ 650 real_gain = (gain * max_gain) / 100; 651 } else { 652 real_gain = gain; 653 } 654 655 if (SDL_SYS_HapticSetGain(haptic, real_gain) < 0) { 656 return -1; 657 } 658 659 return 0; 660} 661 662/* 663 * Makes the device autocenter, 0 disables. 664 */ 665int 666SDL_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) 667{ 668 if (!ValidHaptic(haptic)) { 669 return -1; 670 } 671 672 if ((haptic->supported & SDL_HAPTIC_AUTOCENTER) == 0) { 673 return SDL_SetError("Haptic: Device does not support setting autocenter."); 674 } 675 676 if ((autocenter < 0) || (autocenter > 100)) { 677 return SDL_SetError("Haptic: Autocenter must be between 0 and 100."); 678 } 679 680 if (SDL_SYS_HapticSetAutocenter(haptic, autocenter) < 0) { 681 return -1; 682 } 683 684 return 0; 685} 686 687/* 688 * Pauses the haptic device. 689 */ 690int 691SDL_HapticPause(SDL_Haptic * haptic) 692{ 693 if (!ValidHaptic(haptic)) { 694 return -1; 695 } 696 697 if ((haptic->supported & SDL_HAPTIC_PAUSE) == 0) { 698 return SDL_SetError("Haptic: Device does not support setting pausing."); 699 } 700 701 return SDL_SYS_HapticPause(haptic); 702} 703 704/* 705 * Unpauses the haptic device. 706 */ 707int 708SDL_HapticUnpause(SDL_Haptic * haptic) 709{ 710 if (!ValidHaptic(haptic)) { 711 return -1; 712 } 713 714 if ((haptic->supported & SDL_HAPTIC_PAUSE) == 0) { 715 return 0; /* Not going to be paused, so we pretend it's unpaused. */ 716 } 717 718 return SDL_SYS_HapticUnpause(haptic); 719} 720 721/* 722 * Stops all the currently playing effects. 723 */ 724int 725SDL_HapticStopAll(SDL_Haptic * haptic) 726{ 727 if (!ValidHaptic(haptic)) { 728 return -1; 729 } 730 731 return SDL_SYS_HapticStopAll(haptic); 732} 733 734/* 735 * Checks to see if rumble is supported. 736 */ 737int 738SDL_HapticRumbleSupported(SDL_Haptic * haptic) 739{ 740 if (!ValidHaptic(haptic)) { 741 return -1; 742 } 743 744 /* Most things can use SINE, but XInput only has LEFTRIGHT. */ 745 return ((haptic->supported & (SDL_HAPTIC_SINE|SDL_HAPTIC_LEFTRIGHT)) != 0); 746} 747 748/* 749 * Initializes the haptic device for simple rumble playback. 750 */ 751int 752SDL_HapticRumbleInit(SDL_Haptic * haptic) 753{ 754 SDL_HapticEffect *efx = &haptic->rumble_effect; 755 756 if (!ValidHaptic(haptic)) { 757 return -1; 758 } 759 760 /* Already allocated. */ 761 if (haptic->rumble_id >= 0) { 762 return 0; 763 } 764 765 SDL_zerop(efx); 766 if (haptic->supported & SDL_HAPTIC_SINE) { 767 efx->type = SDL_HAPTIC_SINE; 768 efx->periodic.direction.type = SDL_HAPTIC_CARTESIAN; 769 efx->periodic.period = 1000; 770 efx->periodic.magnitude = 0x4000; 771 efx->periodic.length = 5000; 772 efx->periodic.attack_length = 0; 773 efx->periodic.fade_length = 0; 774 } else if (haptic->supported & SDL_HAPTIC_LEFTRIGHT) { /* XInput? */ 775 efx->type = SDL_HAPTIC_LEFTRIGHT; 776 efx->leftright.length = 5000; 777 efx->leftright.large_magnitude = 0x4000; 778 efx->leftright.small_magnitude = 0x4000; 779 } else { 780 return SDL_SetError("Device doesn't support rumble"); 781 } 782 783 haptic->rumble_id = SDL_HapticNewEffect(haptic, &haptic->rumble_effect); 784 if (haptic->rumble_id >= 0) { 785 return 0; 786 } 787 return -1; 788} 789 790/* 791 * Runs simple rumble on a haptic device 792 */ 793int 794SDL_HapticRumblePlay(SDL_Haptic * haptic, float strength, Uint32 length) 795{ 796 SDL_HapticEffect *efx; 797 Sint16 magnitude; 798 799 if (!ValidHaptic(haptic)) { 800 return -1; 801 } 802 803 if (haptic->rumble_id < 0) { 804 return SDL_SetError("Haptic: Rumble effect not initialized on haptic device"); 805 } 806 807 /* Clamp strength. */ 808 if (strength > 1.0f) { 809 strength = 1.0f; 810 } else if (strength < 0.0f) { 811 strength = 0.0f; 812 } 813 magnitude = (Sint16)(32767.0f*strength); 814 815 efx = &haptic->rumble_effect; 816 if (efx->type == SDL_HAPTIC_SINE) { 817 efx->periodic.magnitude = magnitude; 818 efx->periodic.length = length; 819 } else if (efx->type == SDL_HAPTIC_LEFTRIGHT) { 820 efx->leftright.small_magnitude = efx->leftright.large_magnitude = magnitude; 821 efx->leftright.length = length; 822 } else { 823 SDL_assert(0 && "This should have been caught elsewhere"); 824 } 825 826 if (SDL_HapticUpdateEffect(haptic, haptic->rumble_id, &haptic->rumble_effect) < 0) { 827 return -1; 828 } 829 830 return SDL_HapticRunEffect(haptic, haptic->rumble_id, 1); 831} 832 833/* 834 * Stops the simple rumble on a haptic device. 835 */ 836int 837SDL_HapticRumbleStop(SDL_Haptic * haptic) 838{ 839 if (!ValidHaptic(haptic)) { 840 return -1; 841 } 842 843 if (haptic->rumble_id < 0) { 844 return SDL_SetError("Haptic: Rumble effect not initialized on haptic device"); 845 } 846 847 return SDL_HapticStopEffect(haptic, haptic->rumble_id); 848} 849 850/* vi: set ts=4 sw=4 expandtab: */ 851
[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.