test/testhaptic.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 17 Sep 2014 14:49:36 -0400
changeset 9147 6bf589c8d549
parent 9072 0c1a46c018aa
child 9356 e87d6e1e812a
permissions -rw-r--r--
Haptic: Deal with negative periodic magnitudes (thanks, Elias!).

A negative periodic magnitude doesn't exist in Windows' and MacOS' FF APIs

The periodic magnitude parameter of the SDL Haptic API is based on the Linux
FF API, so it means they are not directly compatible:
'dwMagnitude' is a 'DWORD', which is unsigned.

Fixes Bugzilla #2701.
     1 /*
     2 Copyright (c) 2008, Edgar Simo Serra
     3 All rights reserved.
     4 
     5 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
     6 
     7     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
     8     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
     9     * Neither the name of the Simple Directmedia Layer (SDL) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
    10 
    11 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    12 */
    13 
    14 /*
    15  * includes
    16  */
    17 #include <stdlib.h>
    18 #include <string.h>             /* strstr */
    19 #include <ctype.h>              /* isdigit */
    20 
    21 #include "SDL.h"
    22 
    23 #ifndef SDL_HAPTIC_DISABLED
    24 
    25 #include "SDL_haptic.h"
    26 
    27 static SDL_Haptic *haptic;
    28 
    29 
    30 /*
    31  * prototypes
    32  */
    33 static void abort_execution(void);
    34 static void HapticPrintSupported(SDL_Haptic * haptic);
    35 
    36 
    37 /**
    38  * @brief The entry point of this force feedback demo.
    39  * @param[in] argc Number of arguments.
    40  * @param[in] argv Array of argc arguments.
    41  */
    42 int
    43 main(int argc, char **argv)
    44 {
    45     int i;
    46     char *name;
    47     int index;
    48     SDL_HapticEffect efx[9];
    49     int id[9];
    50     int nefx;
    51     unsigned int supported;
    52 
    53     /* Enable standard application logging */
    54     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
    55 
    56     name = NULL;
    57     index = -1;
    58     if (argc > 1) {
    59         name = argv[1];
    60         if ((strcmp(name, "--help") == 0) || (strcmp(name, "-h") == 0)) {
    61             SDL_Log("USAGE: %s [device]\n"
    62                    "If device is a two-digit number it'll use it as an index, otherwise\n"
    63                    "it'll use it as if it were part of the device's name.\n",
    64                    argv[0]);
    65             return 0;
    66         }
    67 
    68         i = strlen(name);
    69         if ((i < 3) && isdigit(name[0]) && ((i == 1) || isdigit(name[1]))) {
    70             index = atoi(name);
    71             name = NULL;
    72         }
    73     }
    74 
    75     /* Initialize the force feedbackness */
    76     SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_JOYSTICK |
    77              SDL_INIT_HAPTIC);
    78     SDL_Log("%d Haptic devices detected.\n", SDL_NumHaptics());
    79     if (SDL_NumHaptics() > 0) {
    80         /* We'll just use index or the first force feedback device found */
    81         if (name == NULL) {
    82             i = (index != -1) ? index : 0;
    83         }
    84         /* Try to find matching device */
    85         else {
    86             for (i = 0; i < SDL_NumHaptics(); i++) {
    87                 if (strstr(SDL_HapticName(i), name) != NULL)
    88                     break;
    89             }
    90 
    91             if (i >= SDL_NumHaptics()) {
    92                 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to find device matching '%s', aborting.\n",
    93                        name);
    94                 return 1;
    95             }
    96         }
    97 
    98         haptic = SDL_HapticOpen(i);
    99         if (haptic == NULL) {
   100             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create the haptic device: %s\n",
   101                    SDL_GetError());
   102             return 1;
   103         }
   104         SDL_Log("Device: %s\n", SDL_HapticName(i));
   105         HapticPrintSupported(haptic);
   106     } else {
   107         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No Haptic devices found!\n");
   108         return 1;
   109     }
   110 
   111     /* We only want force feedback errors. */
   112     SDL_ClearError();
   113 
   114     /* Create effects. */
   115     memset(&efx, 0, sizeof(efx));
   116     nefx = 0;
   117     supported = SDL_HapticQuery(haptic);
   118 
   119     SDL_Log("\nUploading effects\n");
   120     /* First we'll try a SINE effect. */
   121     if (supported & SDL_HAPTIC_SINE) {
   122         SDL_Log("   effect %d: Sine Wave\n", nefx);
   123         efx[nefx].type = SDL_HAPTIC_SINE;
   124         efx[nefx].periodic.period = 1000;
   125         efx[nefx].periodic.magnitude = -0x2000;    /* Negative magnitude and ...                      */
   126         efx[nefx].periodic.phase = 18000;          /* ... 180 degrees phase shift => cancel eachother */
   127         efx[nefx].periodic.length = 5000;
   128         efx[nefx].periodic.attack_length = 1000;
   129         efx[nefx].periodic.fade_length = 1000;
   130         id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
   131         if (id[nefx] < 0) {
   132             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
   133             abort_execution();
   134         }
   135         nefx++;
   136     }
   137     /* Now we'll try a SAWTOOTHUP */
   138     if (supported & SDL_HAPTIC_SAWTOOTHUP) {
   139         SDL_Log("   effect %d: Sawtooth Up\n", nefx);
   140         efx[nefx].type = SDL_HAPTIC_SAWTOOTHUP;
   141         efx[nefx].periodic.period = 500;
   142         efx[nefx].periodic.magnitude = 0x5000;
   143         efx[nefx].periodic.length = 5000;
   144         efx[nefx].periodic.attack_length = 1000;
   145         efx[nefx].periodic.fade_length = 1000;
   146         id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
   147         if (id[nefx] < 0) {
   148             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
   149             abort_execution();
   150         }
   151         nefx++;
   152     }
   153     
   154     /* Now the classical constant effect. */
   155     if (supported & SDL_HAPTIC_CONSTANT) {
   156         SDL_Log("   effect %d: Constant Force\n", nefx);
   157         efx[nefx].type = SDL_HAPTIC_CONSTANT;
   158         efx[nefx].constant.direction.type = SDL_HAPTIC_POLAR;
   159         efx[nefx].constant.direction.dir[0] = 20000;    /* Force comes from the south-west. */
   160         efx[nefx].constant.length = 5000;
   161         efx[nefx].constant.level = 0x6000;
   162         efx[nefx].constant.attack_length = 1000;
   163         efx[nefx].constant.fade_length = 1000;
   164         id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
   165         if (id[nefx] < 0) {
   166             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
   167             abort_execution();
   168         }
   169         nefx++;
   170     }
   171     
   172     /* The cute spring effect. */
   173     if (supported & SDL_HAPTIC_SPRING) {
   174         SDL_Log("   effect %d: Condition Spring\n", nefx);
   175         efx[nefx].type = SDL_HAPTIC_SPRING;
   176         efx[nefx].condition.length = 5000;
   177         for (i = 0; i < SDL_HapticNumAxes(haptic); i++) {
   178             efx[nefx].condition.right_sat[i] = 0xFFFF;
   179             efx[nefx].condition.left_sat[i] = 0xFFFF;
   180             efx[nefx].condition.right_coeff[i] = 0x2000;
   181             efx[nefx].condition.left_coeff[i] = 0x2000;
   182             efx[nefx].condition.center[i] = 0x1000;     /* Displace the center for it to move. */
   183         }
   184         id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
   185         if (id[nefx] < 0) {
   186             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
   187             abort_execution();
   188         }
   189         nefx++;
   190     }
   191     /* The interesting damper effect. */
   192     if (supported & SDL_HAPTIC_DAMPER) {
   193         SDL_Log("   effect %d: Condition Damper\n", nefx);
   194         efx[nefx].type = SDL_HAPTIC_DAMPER;
   195         efx[nefx].condition.length = 5000;
   196         for (i = 0; i < SDL_HapticNumAxes(haptic); i++) {
   197             efx[nefx].condition.right_sat[i] = 0xFFFF;
   198             efx[nefx].condition.left_sat[i] = 0xFFFF;
   199             efx[nefx].condition.right_coeff[i] = 0x2000;
   200             efx[nefx].condition.left_coeff[i] = 0x2000;
   201         }
   202         id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
   203         if (id[nefx] < 0) {
   204             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
   205             abort_execution();
   206         }
   207         nefx++;
   208     }
   209     /* The pretty awesome inertia effect. */
   210     if (supported & SDL_HAPTIC_INERTIA) {
   211         SDL_Log("   effect %d: Condition Inertia\n", nefx);
   212         efx[nefx].type = SDL_HAPTIC_INERTIA;
   213         efx[nefx].condition.length = 5000;
   214         for (i = 0; i < SDL_HapticNumAxes(haptic); i++) {
   215             efx[nefx].condition.right_sat[i] = 0xFFFF;
   216             efx[nefx].condition.left_sat[i] = 0xFFFF;
   217             efx[nefx].condition.right_coeff[i] = 0x2000;
   218             efx[nefx].condition.left_coeff[i] = 0x2000;
   219             efx[nefx].condition.deadband[i] = 0x1000;    /* 1/16th of axis-range around the center is 'dead'. */
   220         }
   221         id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
   222         if (id[nefx] < 0) {
   223             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
   224             abort_execution();
   225         }
   226         nefx++;
   227     }
   228     /* The hot friction effect. */
   229     if (supported & SDL_HAPTIC_FRICTION) {
   230         SDL_Log("   effect %d: Condition Friction\n", nefx);
   231         efx[nefx].type = SDL_HAPTIC_FRICTION;
   232         efx[nefx].condition.length = 5000;
   233         for (i = 0; i < SDL_HapticNumAxes(haptic); i++) {
   234             efx[nefx].condition.right_sat[i] = 0xFFFF;
   235             efx[nefx].condition.left_sat[i] = 0xFFFF;
   236             efx[nefx].condition.right_coeff[i] = 0x2000;
   237             efx[nefx].condition.left_coeff[i] = 0x2000;
   238         }
   239         id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
   240         if (id[nefx] < 0) {
   241             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
   242             abort_execution();
   243         }
   244         nefx++;
   245     }
   246     
   247     /* Now we'll try a ramp effect */
   248     if (supported & SDL_HAPTIC_RAMP) {
   249         SDL_Log("   effect %d: Ramp\n", nefx);
   250         efx[nefx].type = SDL_HAPTIC_RAMP;
   251         efx[nefx].ramp.direction.type = SDL_HAPTIC_CARTESIAN;
   252         efx[nefx].ramp.direction.dir[0] = 1;     /* Force comes from                 */
   253         efx[nefx].ramp.direction.dir[1] = -1;    /*                  the north-east. */
   254         efx[nefx].ramp.length = 5000;
   255         efx[nefx].ramp.start = 0x4000;
   256         efx[nefx].ramp.end = -0x4000;
   257         efx[nefx].ramp.attack_length = 1000;
   258         efx[nefx].ramp.fade_length = 1000;
   259         id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
   260         if (id[nefx] < 0) {
   261             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
   262             abort_execution();
   263         }
   264         nefx++;
   265     }
   266 
   267     /* Finally we'll try a left/right effect. */
   268     if (supported & SDL_HAPTIC_LEFTRIGHT) {
   269         SDL_Log("   effect %d: Left/Right\n", nefx);
   270         efx[nefx].type = SDL_HAPTIC_LEFTRIGHT;
   271         efx[nefx].leftright.length = 5000;
   272         efx[nefx].leftright.large_magnitude = 0x3000;
   273         efx[nefx].leftright.small_magnitude = 0xFFFF;
   274         id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
   275         if (id[nefx] < 0) {
   276             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
   277             abort_execution();
   278         }
   279         nefx++;
   280     }
   281 
   282 
   283     SDL_Log
   284         ("\nNow playing effects for 5 seconds each with 1 second delay between\n");
   285     for (i = 0; i < nefx; i++) {
   286         SDL_Log("   Playing effect %d\n", i);
   287         SDL_HapticRunEffect(haptic, id[i], 1);
   288         SDL_Delay(6000);        /* Effects only have length 5000 */
   289     }
   290 
   291     /* Quit */
   292     if (haptic != NULL)
   293         SDL_HapticClose(haptic);
   294     SDL_Quit();
   295 
   296     return 0;
   297 }
   298 
   299 
   300 /*
   301  * Cleans up a bit.
   302  */
   303 static void
   304 abort_execution(void)
   305 {
   306     SDL_Log("\nAborting program execution.\n");
   307 
   308     SDL_HapticClose(haptic);
   309     SDL_Quit();
   310 
   311     exit(1);
   312 }
   313 
   314 
   315 /*
   316  * Displays information about the haptic device.
   317  */
   318 static void
   319 HapticPrintSupported(SDL_Haptic * haptic)
   320 {
   321     unsigned int supported;
   322 
   323     supported = SDL_HapticQuery(haptic);
   324     SDL_Log("   Supported effects [%d effects, %d playing]:\n",
   325            SDL_HapticNumEffects(haptic), SDL_HapticNumEffectsPlaying(haptic));
   326     if (supported & SDL_HAPTIC_CONSTANT)
   327         SDL_Log("      constant\n");
   328     if (supported & SDL_HAPTIC_SINE)
   329         SDL_Log("      sine\n");
   330     /* !!! FIXME: put this back when we have more bits in 2.1 */
   331     /* if (supported & SDL_HAPTIC_SQUARE)
   332         SDL_Log("      square\n"); */
   333     if (supported & SDL_HAPTIC_TRIANGLE)
   334         SDL_Log("      triangle\n");
   335     if (supported & SDL_HAPTIC_SAWTOOTHUP)
   336         SDL_Log("      sawtoothup\n");
   337     if (supported & SDL_HAPTIC_SAWTOOTHDOWN)
   338         SDL_Log("      sawtoothdown\n");
   339     if (supported & SDL_HAPTIC_RAMP)
   340         SDL_Log("      ramp\n");
   341     if (supported & SDL_HAPTIC_FRICTION)
   342         SDL_Log("      friction\n");
   343     if (supported & SDL_HAPTIC_SPRING)
   344         SDL_Log("      spring\n");
   345     if (supported & SDL_HAPTIC_DAMPER)
   346         SDL_Log("      damper\n");
   347     if (supported & SDL_HAPTIC_INERTIA)
   348         SDL_Log("      inertia\n");
   349     if (supported & SDL_HAPTIC_CUSTOM)
   350         SDL_Log("      custom\n");
   351     if (supported & SDL_HAPTIC_LEFTRIGHT)
   352         SDL_Log("      left/right\n");
   353     SDL_Log("   Supported capabilities:\n");
   354     if (supported & SDL_HAPTIC_GAIN)
   355         SDL_Log("      gain\n");
   356     if (supported & SDL_HAPTIC_AUTOCENTER)
   357         SDL_Log("      autocenter\n");
   358     if (supported & SDL_HAPTIC_STATUS)
   359         SDL_Log("      status\n");
   360 }
   361 
   362 #else
   363 
   364 int
   365 main(int argc, char *argv[])
   366 {
   367     SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL compiled without Haptic support.\n");
   368     exit(1);
   369 }
   370 
   371 #endif