src/haptic/SDL_haptic.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 25 Aug 2008 09:55:03 +0000
changeset 2713 0906692aa6a4
child 3080 4094b4f1c3a1
permissions -rw-r--r--
Final merge of Google Summer of Code 2008 work...

Force Feedback for SDL
by Edgar Simo, mentored by Ryan C. Gordon
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 2008 Edgar Simo
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 #include "SDL_syshaptic.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     for (i = 0; i < SDL_numhaptics; i++) {
    70         if (SDL_haptics[i] == haptic) {
    71             valid = 1;
    72             break;
    73         }
    74     }
    75 
    76     return valid;
    77 }
    78 
    79 
    80 /*
    81  * Returns the number of available devices.
    82  */
    83 int
    84 SDL_NumHaptics(void)
    85 {
    86     return SDL_numhaptics;
    87 }
    88 
    89 
    90 /*
    91  * Gets the name of a Haptic device by index.
    92  */
    93 const char *
    94 SDL_HapticName(int device_index)
    95 {
    96     if ((device_index < 0) || (device_index >= SDL_numhaptics)) {
    97         SDL_SetError("Haptic: There are %d haptic devices available",
    98                      SDL_numhaptics);
    99         return NULL;
   100     }
   101     return SDL_SYS_HapticName(device_index);
   102 }
   103 
   104 
   105 /*
   106  * Opens a Haptic device.
   107  */
   108 SDL_Haptic *
   109 SDL_HapticOpen(int device_index)
   110 {
   111     int i;
   112     SDL_Haptic *haptic;
   113 
   114     if ((device_index < 0) || (device_index >= SDL_numhaptics)) {
   115         SDL_SetError("Haptic: There are %d haptic devices available",
   116                      SDL_numhaptics);
   117         return NULL;
   118     }
   119 
   120     /* If the haptic is already open, return it */
   121     for (i = 0; SDL_haptics[i]; i++) {
   122         if (device_index == SDL_haptics[i]->index) {
   123             haptic = SDL_haptics[i];
   124             ++haptic->ref_count;
   125             return haptic;
   126         }
   127     }
   128 
   129     /* Create the haptic device */
   130     haptic = (SDL_Haptic *) SDL_malloc((sizeof *haptic));
   131     if (haptic == NULL) {
   132         SDL_OutOfMemory();
   133         return NULL;
   134     }
   135 
   136     /* Initialize the haptic device */
   137     SDL_memset(haptic, 0, (sizeof *haptic));
   138     haptic->index = device_index;
   139     if (SDL_SYS_HapticOpen(haptic) < 0) {
   140         SDL_free(haptic);
   141         return NULL;
   142     }
   143 
   144     /* Disable autocenter and set gain to max. */
   145     if (haptic->supported & SDL_HAPTIC_GAIN)
   146         SDL_HapticSetGain(haptic, 100);
   147     if (haptic->supported & SDL_HAPTIC_AUTOCENTER)
   148         SDL_HapticSetAutocenter(haptic, 0);
   149 
   150     /* Add haptic to list */
   151     ++haptic->ref_count;
   152     for (i = 0; SDL_haptics[i]; i++)
   153         /* Skip to next haptic */ ;
   154     SDL_haptics[i] = haptic;
   155 
   156     return haptic;
   157 }
   158 
   159 
   160 /*
   161  * Returns 1 if the device has been opened.
   162  */
   163 int
   164 SDL_HapticOpened(int device_index)
   165 {
   166     int i, opened;
   167 
   168     opened = 0;
   169     for (i = 0; SDL_haptics[i]; i++) {
   170         if (SDL_haptics[i]->index == (Uint8) device_index) {
   171             opened = 1;
   172             break;
   173         }
   174     }
   175     return opened;
   176 }
   177 
   178 
   179 /*
   180  * Returns the index to a haptic device.
   181  */
   182 int
   183 SDL_HapticIndex(SDL_Haptic * haptic)
   184 {
   185     if (!ValidHaptic(haptic)) {
   186         return -1;
   187     }
   188 
   189     return haptic->index;
   190 }
   191 
   192 
   193 /*
   194  * Returns SDL_TRUE if mouse is haptic, SDL_FALSE if it isn't.
   195  */
   196 int
   197 SDL_MouseIsHaptic(void)
   198 {
   199     if (SDL_SYS_HapticMouse() < 0)
   200         return SDL_FALSE;
   201     return SDL_TRUE;
   202 }
   203 
   204 
   205 /*
   206  * Returns the haptic device if mouse is haptic or NULL elsewise.
   207  */
   208 SDL_Haptic *
   209 SDL_HapticOpenFromMouse(void)
   210 {
   211     int device_index;
   212 
   213     device_index = SDL_SYS_HapticMouse();
   214 
   215     if (device_index < 0) {
   216         SDL_SetError("Haptic: Mouse isn't a haptic device.");
   217         return NULL;
   218     }
   219 
   220     return SDL_HapticOpen(device_index);
   221 }
   222 
   223 
   224 /*
   225  * Returns SDL_TRUE if joystick has haptic features.
   226  */
   227 int
   228 SDL_JoystickIsHaptic(SDL_Joystick * joystick)
   229 {
   230     int ret;
   231 
   232     /* Must be a valid joystick */
   233     if (!SDL_PrivateJoystickValid(&joystick)) {
   234         return -1;
   235     }
   236 
   237     ret = SDL_SYS_JoystickIsHaptic(joystick);
   238 
   239     if (ret > 0)
   240         return SDL_TRUE;
   241     else if (ret == 0)
   242         return SDL_FALSE;
   243     else
   244         return -1;
   245 }
   246 
   247 
   248 /*
   249  * Opens a haptic device from a joystick.
   250  */
   251 SDL_Haptic *
   252 SDL_HapticOpenFromJoystick(SDL_Joystick * joystick)
   253 {
   254     int i;
   255     SDL_Haptic *haptic;
   256 
   257     /* Must be a valid joystick */
   258     if (!SDL_PrivateJoystickValid(&joystick)) {
   259         return NULL;
   260     }
   261 
   262     /* Joystick must be haptic */
   263     if (SDL_SYS_JoystickIsHaptic(joystick) <= 0) {
   264         return NULL;
   265     }
   266 
   267     /* Check to see if joystick's haptic is already open */
   268     for (i = 0; SDL_haptics[i]; i++) {
   269         if (SDL_SYS_JoystickSameHaptic(SDL_haptics[i], joystick)) {
   270             haptic = SDL_haptics[i];
   271             ++haptic->ref_count;
   272             return haptic;
   273         }
   274     }
   275 
   276     /* Create the haptic device */
   277     haptic = (SDL_Haptic *) SDL_malloc((sizeof *haptic));
   278     if (haptic == NULL) {
   279         SDL_OutOfMemory();
   280         return NULL;
   281     }
   282 
   283     /* Initialize the haptic device */
   284     SDL_memset(haptic, 0, sizeof(SDL_Haptic));
   285     if (SDL_SYS_HapticOpenFromJoystick(haptic, joystick) < 0) {
   286         SDL_free(haptic);
   287         return NULL;
   288     }
   289 
   290     /* Add haptic to list */
   291     ++haptic->ref_count;
   292     for (i = 0; SDL_haptics[i]; i++)
   293         /* Skip to next haptic */ ;
   294     SDL_haptics[i] = haptic;
   295 
   296     return haptic;
   297 }
   298 
   299 
   300 /*
   301  * Closes a SDL_Haptic device.
   302  */
   303 void
   304 SDL_HapticClose(SDL_Haptic * haptic)
   305 {
   306     int i;
   307 
   308     /* Must be valid */
   309     if (!ValidHaptic(haptic)) {
   310         return;
   311     }
   312 
   313     /* Check if it's still in use */
   314     if (--haptic->ref_count < 0) {
   315         return;
   316     }
   317 
   318     /* Close it, properly removing effects if needed */
   319     for (i = 0; i < haptic->neffects; i++) {
   320         if (haptic->effects[i].hweffect != NULL) {
   321             SDL_HapticDestroyEffect(haptic, i);
   322         }
   323     }
   324     SDL_SYS_HapticClose(haptic);
   325 
   326     /* Remove from the list */
   327     for (i = 0; SDL_haptics[i]; ++i) {
   328         if (haptic == SDL_haptics[i]) {
   329             SDL_haptics[i] = NULL;
   330             SDL_memcpy(&SDL_haptics[i], &SDL_haptics[i + 1],
   331                        (SDL_numhaptics - i) * sizeof(haptic));
   332             break;
   333         }
   334     }
   335 
   336     /* Free */
   337     SDL_free(haptic);
   338 }
   339 
   340 /*
   341  * Cleans up after the subsystem.
   342  */
   343 void
   344 SDL_HapticQuit(void)
   345 {
   346     SDL_SYS_HapticQuit();
   347     if (SDL_haptics != NULL) {
   348         SDL_free(SDL_haptics);
   349         SDL_haptics = NULL;
   350     }
   351     SDL_numhaptics = 0;
   352 }
   353 
   354 /*
   355  * Returns the number of effects a haptic device has.
   356  */
   357 int
   358 SDL_HapticNumEffects(SDL_Haptic * haptic)
   359 {
   360     if (!ValidHaptic(haptic)) {
   361         return -1;
   362     }
   363 
   364     return haptic->neffects;
   365 }
   366 
   367 
   368 /*
   369  * Returns the number of effects a haptic device can play.
   370  */
   371 int
   372 SDL_HapticNumEffectsPlaying(SDL_Haptic * haptic)
   373 {
   374     if (!ValidHaptic(haptic)) {
   375         return -1;
   376     }
   377 
   378     return haptic->nplaying;
   379 }
   380 
   381 
   382 /*
   383  * Returns supported effects by the device.
   384  */
   385 unsigned int
   386 SDL_HapticQuery(SDL_Haptic * haptic)
   387 {
   388     if (!ValidHaptic(haptic)) {
   389         return -1;
   390     }
   391 
   392     return haptic->supported;
   393 }
   394 
   395 
   396 /*
   397  * Returns the number of axis on the device.
   398  */
   399 int
   400 SDL_HapticNumAxes(SDL_Haptic * haptic)
   401 {
   402     if (!ValidHaptic(haptic)) {
   403         return -1;
   404     }
   405 
   406     return haptic->naxes;
   407 }
   408 
   409 /*
   410  * Checks to see if the device can support the effect.
   411  */
   412 int
   413 SDL_HapticEffectSupported(SDL_Haptic * haptic, SDL_HapticEffect * effect)
   414 {
   415     if (!ValidHaptic(haptic)) {
   416         return -1;
   417     }
   418 
   419     if ((haptic->supported & effect->type) != 0)
   420         return SDL_TRUE;
   421     return SDL_FALSE;
   422 }
   423 
   424 /*
   425  * Creates a new haptic effect.
   426  */
   427 int
   428 SDL_HapticNewEffect(SDL_Haptic * haptic, SDL_HapticEffect * effect)
   429 {
   430     int i;
   431 
   432     /* Check for device validity. */
   433     if (!ValidHaptic(haptic)) {
   434         return -1;
   435     }
   436 
   437     /* Check to see if effect is supported */
   438     if (SDL_HapticEffectSupported(haptic, effect) == SDL_FALSE) {
   439         SDL_SetError("Haptic: Effect not supported by haptic device.");
   440         return -1;
   441     }
   442 
   443     /* See if there's a free slot */
   444     for (i = 0; i < haptic->neffects; i++) {
   445         if (haptic->effects[i].hweffect == NULL) {
   446 
   447             /* Now let the backend create the real effect */
   448             if (SDL_SYS_HapticNewEffect(haptic, &haptic->effects[i], effect)
   449                 != 0) {
   450                 return -1;      /* Backend failed to create effect */
   451             }
   452 
   453             SDL_memcpy(&haptic->effects[i].effect, effect,
   454                        sizeof(SDL_HapticEffect));
   455             return i;
   456         }
   457     }
   458 
   459     SDL_SetError("Haptic: Device has no free space left.");
   460     return -1;
   461 }
   462 
   463 /*
   464  * Checks to see if an effect is valid.
   465  */
   466 static int
   467 ValidEffect(SDL_Haptic * haptic, int effect)
   468 {
   469     if ((effect < 0) || (effect >= haptic->neffects)) {
   470         SDL_SetError("Haptic: Invalid effect identifier.");
   471         return 0;
   472     }
   473     return 1;
   474 }
   475 
   476 /*
   477  * Updates an effect.
   478  */
   479 int
   480 SDL_HapticUpdateEffect(SDL_Haptic * haptic, int effect,
   481                        SDL_HapticEffect * data)
   482 {
   483     if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) {
   484         return -1;
   485     }
   486 
   487     /* Can't change type dynamically. */
   488     if (data->type != haptic->effects[effect].effect.type) {
   489         SDL_SetError("Haptic: Updating effect type is illegal.");
   490         return -1;
   491     }
   492 
   493     /* Updates the effect */
   494     if (SDL_SYS_HapticUpdateEffect(haptic, &haptic->effects[effect], data) <
   495         0) {
   496         return -1;
   497     }
   498 
   499     SDL_memcpy(&haptic->effects[effect].effect, data,
   500                sizeof(SDL_HapticEffect));
   501     return 0;
   502 }
   503 
   504 
   505 /*
   506  * Runs the haptic effect on the device.
   507  */
   508 int
   509 SDL_HapticRunEffect(SDL_Haptic * haptic, int effect, Uint32 iterations)
   510 {
   511     if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) {
   512         return -1;
   513     }
   514 
   515     /* Run the effect */
   516     if (SDL_SYS_HapticRunEffect(haptic, &haptic->effects[effect], iterations)
   517         < 0) {
   518         return -1;
   519     }
   520 
   521     return 0;
   522 }
   523 
   524 /*
   525  * Stops the haptic effect on the device.
   526  */
   527 int
   528 SDL_HapticStopEffect(SDL_Haptic * haptic, int effect)
   529 {
   530     if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) {
   531         return -1;
   532     }
   533 
   534     /* Stop the effect */
   535     if (SDL_SYS_HapticStopEffect(haptic, &haptic->effects[effect]) < 0) {
   536         return -1;
   537     }
   538 
   539     return 0;
   540 }
   541 
   542 /*
   543  * Gets rid of a haptic effect.
   544  */
   545 void
   546 SDL_HapticDestroyEffect(SDL_Haptic * haptic, int effect)
   547 {
   548     if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) {
   549         return;
   550     }
   551 
   552     /* Not allocated */
   553     if (haptic->effects[effect].hweffect == NULL) {
   554         return;
   555     }
   556 
   557     SDL_SYS_HapticDestroyEffect(haptic, &haptic->effects[effect]);
   558 }
   559 
   560 /*
   561  * Gets the status of a haptic effect.
   562  */
   563 int
   564 SDL_HapticGetEffectStatus(SDL_Haptic * haptic, int effect)
   565 {
   566     if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) {
   567         return -1;
   568     }
   569 
   570     if ((haptic->supported & SDL_HAPTIC_STATUS) == 0) {
   571         SDL_SetError("Haptic: Device does not support status queries.");
   572         return -1;
   573     }
   574 
   575     return SDL_SYS_HapticGetEffectStatus(haptic, &haptic->effects[effect]);
   576 }
   577 
   578 /*
   579  * Sets the global gain of the device.
   580  */
   581 int
   582 SDL_HapticSetGain(SDL_Haptic * haptic, int gain)
   583 {
   584     const char *env;
   585     int real_gain, max_gain;
   586 
   587     if (!ValidHaptic(haptic)) {
   588         return -1;
   589     }
   590 
   591     if ((haptic->supported & SDL_HAPTIC_GAIN) == 0) {
   592         SDL_SetError("Haptic: Device does not support setting gain.");
   593         return -1;
   594     }
   595 
   596     if ((gain < 0) || (gain > 100)) {
   597         SDL_SetError("Haptic: Gain must be between 0 and 100.");
   598         return -1;
   599     }
   600 
   601     /* We use the envvar to get the maximum gain. */
   602     env = SDL_getenv("SDL_HAPTIC_GAIN_MAX");
   603     if (env != NULL) {
   604         max_gain = SDL_atoi(env);
   605 
   606         /* Check for sanity. */
   607         if (max_gain < 0)
   608             max_gain = 0;
   609         else if (max_gain > 100)
   610             max_gain = 100;
   611 
   612         /* We'll scale it linearly with SDL_HAPTIC_GAIN_MAX */
   613         real_gain = (gain * max_gain) / 100;
   614     } else {
   615         real_gain = gain;
   616     }
   617 
   618     if (SDL_SYS_HapticSetGain(haptic, real_gain) < 0) {
   619         return -1;
   620     }
   621 
   622     return 0;
   623 }
   624 
   625 /*
   626  * Makes the device autocenter, 0 disables.
   627  */
   628 int
   629 SDL_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
   630 {
   631     if (!ValidHaptic(haptic)) {
   632         return -1;
   633     }
   634 
   635     if ((haptic->supported & SDL_HAPTIC_AUTOCENTER) == 0) {
   636         SDL_SetError("Haptic: Device does not support setting autocenter.");
   637         return -1;
   638     }
   639 
   640     if ((autocenter < 0) || (autocenter > 100)) {
   641         SDL_SetError("Haptic: Autocenter must be between 0 and 100.");
   642         return -1;
   643     }
   644 
   645     if (SDL_SYS_HapticSetAutocenter(haptic, autocenter) < 0) {
   646         return -1;
   647     }
   648 
   649     return 0;
   650 }
   651 
   652 /*
   653  * Pauses the haptic device.
   654  */
   655 int
   656 SDL_HapticPause(SDL_Haptic * haptic)
   657 {
   658     if (!ValidHaptic(haptic)) {
   659         return -1;
   660     }
   661 
   662     if ((haptic->supported & SDL_HAPTIC_PAUSE) == 0) {
   663         SDL_SetError("Haptic: Device does not support setting pausing.");
   664         return -1;
   665     }
   666 
   667     return SDL_SYS_HapticPause(haptic);
   668 }
   669 
   670 /*
   671  * Unpauses the haptic device.
   672  */
   673 int
   674 SDL_HapticUnpause(SDL_Haptic * haptic)
   675 {
   676     if (!ValidHaptic(haptic)) {
   677         return -1;
   678     }
   679 
   680     if ((haptic->supported & SDL_HAPTIC_PAUSE) == 0) {
   681         return 0;               /* Not going to be paused, so we pretend it's unpaused. */
   682     }
   683 
   684     return SDL_SYS_HapticUnpause(haptic);
   685 }
   686 
   687 /*
   688  * Stops all the currently playing effects.
   689  */
   690 int
   691 SDL_HapticStopAll(SDL_Haptic * haptic)
   692 {
   693     if (!ValidHaptic(haptic)) {
   694         return -1;
   695     }
   696 
   697     return SDL_SYS_HapticStopAll(haptic);
   698 }