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