src/haptic/win32/SDL_syshaptic.c
branchgsoc2008_force_feedback
changeset 2551 f010e1d4e431
child 2561 3696b9ce8a37
equal deleted inserted replaced
2550:b5b8a7f4a965 2551:f010e1d4e431
       
     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 #ifdef SDL_HAPTIC_DINPUT
       
    25 
       
    26 #include "SDL_haptic.h"
       
    27 #include "../SDL_syshaptic.h"
       
    28 #include "SDL_joystick.h"
       
    29 #include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */
       
    30 /*#include "../../joystick/win32/SDL_sysjoystick_c.h"*/ /* For joystick hwdata */ 
       
    31 
       
    32 #define WIN32_LEAN_AND_MEAN
       
    33 #include <windows.h>
       
    34 
       
    35 #define DIRECTINPUT_VERSION 0x0500
       
    36 #include <dinput.h>
       
    37 #include <dxerr9.h>          /* From DirectX SDK 9c */
       
    38 #ifdef _MSC_VER
       
    39 #   pragma comment (lib, "dxerr9.lib")
       
    40 #endif /* _MSC_VER */
       
    41 
       
    42     /* an ISO hack for VisualC++ */
       
    43 #ifdef _MSC_VER
       
    44 #define   snprintf   _snprintf
       
    45 #endif /* _MSC_VER */
       
    46 
       
    47 
       
    48 #define MAX_HAPTICS  32
       
    49 
       
    50 
       
    51 /*
       
    52  * List of available haptic devices.
       
    53  */
       
    54 static struct
       
    55 {
       
    56    DIDEVICEINSTANCE instance;
       
    57    SDL_Haptic *haptic;
       
    58 } SDL_hapticlist[MAX_HAPTICS];
       
    59 
       
    60 
       
    61 /*
       
    62  * Haptic system hardware data.
       
    63  */
       
    64 struct haptic_hwdata
       
    65 {
       
    66    LPDIRECTINPUTDEVICE2 device;
       
    67    DIDEVCAPS capabilities;
       
    68 };
       
    69 
       
    70 
       
    71 /*
       
    72  * Haptic system effect data.
       
    73  */
       
    74 struct haptic_hweffect
       
    75 {
       
    76    DIEFFECT effect;
       
    77 };
       
    78 
       
    79 
       
    80 /*
       
    81  * Internal stuff.
       
    82  */
       
    83 static LPDIRECTINPUT dinput = NULL;
       
    84 
       
    85 
       
    86 /*
       
    87  * Prototypes.
       
    88  */
       
    89 static BOOL CALLBACK EnumHapticsCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext);
       
    90 
       
    91 
       
    92 /* 
       
    93  * Like SDL_SetError but for DX error codes.
       
    94  */
       
    95 static void
       
    96 DI_SetError(const char *str, HRESULT err)
       
    97 {
       
    98    SDL_SetError( "Haptic: %s - %s: %s", str,
       
    99                  DXGetErrorString9(err),
       
   100                  DXGetErrorDescription9(err));
       
   101 }
       
   102 
       
   103 
       
   104 /*
       
   105  * Initializes the haptic subsystem.
       
   106  */
       
   107 int
       
   108 SDL_SYS_HapticInit(void)
       
   109 {
       
   110    HRESULT ret;
       
   111 
       
   112    if (dinput != NULL) { /* Already open. */
       
   113       SDL_SetError("Haptic: SubSystem already open.");
       
   114       return -1;
       
   115    }
       
   116 
       
   117    /* Clear all the memory. */
       
   118    SDL_memset(SDL_hapticlist, 0, sizeof(SDL_hapticlist));
       
   119 
       
   120    SDL_numhaptics = 0;
       
   121 
       
   122    ret = CoInitialize(NULL);
       
   123    if (FAILED(ret)) {
       
   124       DI_SetError("Coinitialize",ret);
       
   125       return -1;
       
   126    }
       
   127 
       
   128    ret = CoCreateInstance(&CLSID_DirectInput, NULL, CLSCTX_INPROC_SERVER,
       
   129                           &IID_IDirectInput, &dinput);
       
   130    if (FAILED(ret)) {
       
   131       DI_SetError("CoCreateInstance",ret);
       
   132       return -1;
       
   133    }
       
   134 
       
   135    /* Because we used CoCreateInstance, we need to Initialize it, first. */
       
   136    ret = IDirectInput_Initialize(dinput, SDL_Instance, DIRECTINPUT_VERSION);
       
   137    if (FAILED(ret)) {
       
   138       DI_SetError("Initializing DirectInput device",ret);
       
   139       return -1;
       
   140    }
       
   141 
       
   142    /* Look for haptic devices. */
       
   143    ret = IDirectInput_EnumDevices( dinput,
       
   144                                    DIDEVTYPE_ALL,
       
   145                                    EnumJoysticksCallback,
       
   146                                    NULL, DIEDFL_FORCEFEEDBACK | DIEDFL_ATTACHEDONLY);
       
   147    if (FAILED(ret)) {
       
   148       DI_SetError("Enumerating DirectInput devices",ret);
       
   149       return -1;
       
   150    }
       
   151 
       
   152    return SDL_numhaptics;
       
   153 }
       
   154 
       
   155 /*
       
   156  * Callback to find the haptic devices.
       
   157  */
       
   158 static BOOL CALLBACK
       
   159 EnumHapticsCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
       
   160 {
       
   161    memcpy(&SDL_hapticlist[SDL_numhaptics].instance, pdidInstance,
       
   162          sizeof(DIDEVICEINSTANCE));
       
   163    SDL_numhaptics++;
       
   164 
       
   165    if (SDL_numhaptics >= MAX_HAPTICS)
       
   166       return DIENUM_STOP;
       
   167 
       
   168    return DIENUM_CONTINUE;
       
   169 }
       
   170 
       
   171 
       
   172 /*
       
   173  * Return the name of a haptic device, does not need to be opened.
       
   174  */
       
   175 const char *
       
   176 SDL_SYS_HapticName(int index)
       
   177 {
       
   178    return SDL_hapticlist[index].instance.tszProductName;
       
   179 }
       
   180 
       
   181 
       
   182 /*
       
   183  * Callback to get all supported effects.
       
   184  */
       
   185 #define EFFECT_TEST(e,s)   \
       
   186 if (pei->guid == (e))   \
       
   187    haptic->supported |= (s)
       
   188 static BOOL CALLBACK
       
   189 DI_EffectCallback(LPCDIEffectInfo pei, LPVOID pv)
       
   190 {
       
   191    /* Prepare the haptic device. */
       
   192    SDL_Haptic *haptic = (SDL_Haptic*) pv;
       
   193    haptic->supported = 0;
       
   194 
       
   195    /* Get supported. */
       
   196    EFFECT_TEST(GUID_Spring,         SDL_HAPTIC_SPRING);
       
   197    EFFECT_TEST(GUID_Damper,         SDL_HAPTIC_DAMPER);
       
   198    EFFECT_TEST(GUID_Inertia,        SDL_HAPTIC_INERTIA);
       
   199    EFFECT_TEST(GUID_Friction,       SDL_HAPTIC_FRICTION);
       
   200    EFFECT_TEST(GUID_ConstantForce,  SDL_HAPTIC_CONSTANT);
       
   201    EFFECT_TEST(GUID_CustomForce,    SDL_HAPTIC_CUSTOM);
       
   202    EFFECT_TEST(GUID_Sine,           SDL_HAPTIC_SINE);
       
   203    EFFECT_TEST(GUID_Square,         SDL_HAPTIC_SQUARE);
       
   204    EFFECT_TEST(GUID_Triangle,       SDL_HAPTIC_TRIANGLE);
       
   205    EFFECT_TEST(GUID_SawtoothUp,     SDL_HAPTIC_SAWTOOTHUP);
       
   206    EFFECT_TEST(GUID_SawtoothDown,   SDL_HAPTIC_SAWTOOTHDOWN);
       
   207    EFFECT_TEST(GUID_RampForce,      SDL_HAPTIC_RAMP);
       
   208   
       
   209    /* Check for more. */
       
   210    return DIENUM_CONTINUE;
       
   211 }
       
   212 
       
   213 
       
   214 /*
       
   215  * Opens the haptic device from the file descriptor.
       
   216  *
       
   217  *    Steps:
       
   218  *       - Open temporary DirectInputDevice interface.
       
   219  *       - Create DirectInputDevice2 interface.
       
   220  *       - Release DirectInputDevice interface.
       
   221  *       - Acquire exclusiveness.
       
   222  *       - Reset actuators.
       
   223  *       - Get supported featuers.
       
   224  */
       
   225 static int
       
   226 SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic, DIDEVICEINSTANCE instance)
       
   227 {
       
   228    HRESULT ret;
       
   229    LPDIRECTINPUTDEVICE device;
       
   230    DIPROPDWORD dipdw;
       
   231 
       
   232    /* Allocate the hwdata */
       
   233    haptic->hwdata = (struct haptic_hwdata *)
       
   234          SDL_malloc(sizeof(*haptic->hwdata));
       
   235    if (haptic->hwdata == NULL) {
       
   236       SDL_OutOfMemory();
       
   237       goto creat_err;
       
   238    }
       
   239    SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
       
   240   
       
   241    /* Open the device */
       
   242    ret = IDirectInput_CreateDevice( dinput, &instance,
       
   243                                     guidInstance, &device, NULL);
       
   244    if (FAILED(ret)) {
       
   245       DI_SetError("Creating DirectInput device",ret);
       
   246       goto creat_err;
       
   247    }
       
   248 
       
   249    /* Now get the IDirectInputDevice2 interface, instead. */
       
   250    ret = IDirectInputDevice_QueryInterface( device,
       
   251                                             &IID_IDirectInputDevice2,
       
   252                                             haptic->hwdata->device );
       
   253    /* Done with the temporary one now. */
       
   254    IDirectInputDevice_Release(device);
       
   255    if (FAILED(ret)) {
       
   256       DI_SetError("Querying DirectInput interface",ret);
       
   257       goto creat_err;
       
   258    }
       
   259 
       
   260    /* Acquire the device. */
       
   261    ret = IDirectInputDevice2_Acquire(haptic->hwdata->device);
       
   262    if (FAILED(ret)) {
       
   263       DI_SetError("Acquiring DirectInput device",ret);
       
   264       goto query_err;
       
   265    }
       
   266 
       
   267    /* Grab it exclusively to use force feedback stuff. */
       
   268    ret =IDirectInputDevice2_SetCooperativeLevel( haptic->hwdata->device,
       
   269                                                  SDL_Window,
       
   270                                                  DISCL_EXCLUSIVE | DISCL_BACKGROUND );
       
   271    if (FAILED(ret)) {
       
   272       DI_SetError("Setting cooperative level to exclusive",ret);
       
   273       goto acquire_err;
       
   274    }
       
   275 
       
   276    /* Reset all actuators - just in case. */
       
   277    ret = IDirectInputDevice2_SendForceFeedbackCommand( haptic->hwdata->device,
       
   278                                                        DISFFC_RESET );
       
   279    if (FAILED(ret)) {
       
   280       DI_SetError("Resetting device",ret);
       
   281       goto acquire_err;
       
   282    }
       
   283 
       
   284 
       
   285    /* Enabling actuators. */
       
   286    ret = IDirectInputDevice2_SendForceFeedbackCommand( haptic->hwdata->device,
       
   287                                                        DISFFC_SETACTUATORSON );
       
   288    if (FAILED(ret)) {
       
   289       DI_SetError("Enabling actuators",ret);
       
   290       goto acquire_err;
       
   291    }
       
   292 
       
   293 
       
   294    /* Get capabilities. */
       
   295    ret = IDirectInputDevice2_GetCapabilities( haptic->hwdata->device,
       
   296                                               haptic->hwdata->capabilities );
       
   297    if (FAILED(ret)) {
       
   298       DI_SetError("Getting device capabilities",ret);
       
   299       goto acquire_err;
       
   300    }
       
   301 
       
   302 
       
   303    /* Get supported effects. */
       
   304    ret = IDirectInput_EnumEffects( DI_EffectCallback, haptic, DIEFT_ALL);
       
   305    if (FAILED(ret)) {
       
   306       DI_SetError("Enumerating supported effects",ret);
       
   307       goto acquire_err;
       
   308    }
       
   309    if (haptic->supported == 0) { /* Error since device supports nothing. */
       
   310       SDL_SetError("Haptic: Internal error on finding supported effects.");
       
   311       goto acquire_err;
       
   312    }
       
   313 
       
   314    /* Check autogain and autocenter. */
       
   315    dipdw.diph.dwSize       = sizeof(DIPROPDWORD); 
       
   316    dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); 
       
   317    dipdw.diph.dwObj        = 0;
       
   318    dipdw.diph.dwHow        = DIPH_DEVICE;
       
   319    dipdw.dwData            = 10000;
       
   320    ret = IDirectInputDevice2_SetProperty( haptic->hwdata->device,        
       
   321                                           DIPROP_FFGAIN, &dipdw.diph );
       
   322    if (FAILED(ret)) {
       
   323       if (ret != DIERR_UNSUPPORTED) {
       
   324          DI_SetError("Checking gain",ret);
       
   325          goto acquire_err;
       
   326       }
       
   327    }
       
   328    else { /* Gain is supported. */
       
   329       haptic->supported |= SDL_HAPTIC_GAIN;
       
   330    }
       
   331    dipdw.dwData            = DIPROPAUTOCENTER_OFF;
       
   332    ret = IDirectInputDevice2_SetProperty( haptic->hwdata->device,
       
   333                                           DIPROP_AUTOCENTER, &dipdw.diph );
       
   334    if (FAILED(ret)) {
       
   335       if (ret != DIERR_UNSUPPORTED) {
       
   336          DI_SetError("Checking autocenter",ret);
       
   337          goto acquire_err;
       
   338       }
       
   339    }
       
   340    else { /* Autocenter is supported. */
       
   341       haptic->supported |= SDL_HAPTIC_AUTOCENTER;
       
   342    }
       
   343 
       
   344 
       
   345    /* Check maximum effects. */
       
   346    haptic->neffects = 128; /* TODO actually figure this out. */
       
   347    haptic->nplaying = 128;
       
   348 
       
   349 
       
   350    /* Prepare effects memory. */
       
   351    haptic->effects = (struct haptic_effect *)
       
   352          SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
       
   353    if (haptic->effects == NULL) {
       
   354       SDL_OutOfMemory();
       
   355       goto acquire_err;
       
   356    }
       
   357    /* Clear the memory */
       
   358    SDL_memset(haptic->effects, 0,
       
   359          sizeof(struct haptic_effect) * haptic->neffects);
       
   360    
       
   361    return 0;
       
   362    
       
   363    /* Error handling */
       
   364 open_err:
       
   365    IDirectInputDevice_Release(device);
       
   366    goto creat_err;
       
   367 acquire_err:
       
   368    IDirectInputDevice2_Unacquire(haptic->hwdata->device);
       
   369 query_err:
       
   370    IDirectInputDevice2_Release(haptic->hwdata->device);   
       
   371 creat_err:
       
   372    if (haptic->hwdata != NULL) {
       
   373       free(haptic->hwdata);
       
   374       haptic->hwdata = NULL;                                              
       
   375    }
       
   376    return -1;
       
   377 
       
   378 }
       
   379 
       
   380 
       
   381 /*
       
   382  * Opens a haptic device for usage.
       
   383  */
       
   384 int
       
   385 SDL_SYS_HapticOpen(SDL_Haptic * haptic)
       
   386 {
       
   387    return SDL_SYS_HapticOpenFromInstance( haptic,
       
   388          SDL_hapticlist[haptic->index].instance );
       
   389 }
       
   390 
       
   391 
       
   392 /*
       
   393  * Opens a haptic device from first mouse it finds for usage.
       
   394  */
       
   395 int
       
   396 SDL_SYS_HapticMouse(void)
       
   397 {
       
   398    return -1;
       
   399 }
       
   400 
       
   401 
       
   402 /*
       
   403  * Checks to see if a joystick has haptic features.
       
   404  */
       
   405 int
       
   406 SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
       
   407 {
       
   408    return SDL_FALSE;
       
   409 }
       
   410 
       
   411 
       
   412 /*
       
   413  * Checks to see if the haptic device and joystick and in reality the same.
       
   414  */
       
   415 int
       
   416 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
       
   417 {
       
   418    return 0;
       
   419 }
       
   420 
       
   421 
       
   422 /*
       
   423  * Opens a SDL_Haptic from a SDL_Joystick.
       
   424  */
       
   425 int
       
   426 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
       
   427 {
       
   428    return -1;
       
   429 }
       
   430 
       
   431 
       
   432 /*
       
   433  * Closes the haptic device.
       
   434  */
       
   435 void
       
   436 SDL_SYS_HapticClose(SDL_Haptic * haptic)
       
   437 {
       
   438    int i;
       
   439 
       
   440    if (haptic->hwdata) {
       
   441 
       
   442       /* Free the effects. */
       
   443       for (i=0; i<haptic->neffects; i++) {        
       
   444          if (haptic->effects[i].hweffect != NULL) {
       
   445             SDL_SYS_HapticFreeFFEFFECT( &haptic->effects[i].hweffect->effect,
       
   446                                         haptic->effects[i].effect.type );
       
   447             SDL_free(haptic->effects[i].hweffect);
       
   448          } 
       
   449       }    
       
   450       SDL_free(haptic->effects);
       
   451       haptic->neffects = 0;
       
   452 
       
   453       /* Clean up */
       
   454       IDirectInputDevice2_Unacquire(haptic->hwdata->device);
       
   455       IDirectInputDevice2_Release(haptic->hwdata->device);   
       
   456 
       
   457       /* Free */
       
   458       SDL_free(haptic->hwdata);
       
   459       haptic->hwdata = NULL;
       
   460    }
       
   461 }
       
   462 
       
   463 
       
   464 /* 
       
   465  * Clean up after system specific haptic stuff
       
   466  */
       
   467 void
       
   468 SDL_SYS_HapticQuit(void)
       
   469 {
       
   470    IDirectInput_Release(dinput);
       
   471    dinput = NULL;
       
   472 }
       
   473 
       
   474 
       
   475 /*
       
   476  * Sets the direction.
       
   477  */
       
   478 static int
       
   479 SDL_SYS_SetDirection( FFEFFECT * effect, SDL_HapticDirection *dir, int naxes )
       
   480 {
       
   481    LONG *rglDir;
       
   482 
       
   483    /* Handle no axes a part. */
       
   484    if (naxes == 0) {
       
   485       effect->rglDirection = NULL;
       
   486       return 0;
       
   487    }
       
   488 
       
   489    /* Has axes. */
       
   490    rglDir = SDL_malloc( sizeof(LONG) * naxes );
       
   491    if (rglDir == NULL) {
       
   492       SDL_OutOfMemory();
       
   493       return -1;
       
   494    }
       
   495    SDL_memset( rglDir, 0, sizeof(LONG) * naxes );
       
   496    effect->rglDirection = rglDir;
       
   497 
       
   498    switch (dir->type) {
       
   499       case SDL_HAPTIC_POLAR:
       
   500          effect->dwFlags |= FFEFF_POLAR;
       
   501          rglDir[0] = dir->dir[0];
       
   502          return 0;
       
   503       case SDL_HAPTIC_CARTESIAN:
       
   504          effect->dwFlags |= FFEFF_CARTESIAN;
       
   505          rglDir[0] = dir->dir[0];
       
   506          rglDir[1] = dir->dir[1];
       
   507          rglDir[2] = dir->dir[2];
       
   508          return 0;
       
   509       case SDL_HAPTIC_SPHERICAL:
       
   510          effect->dwFlags |= FFEFF_SPHERICAL;
       
   511          rglDir[0] = dir->dir[0];
       
   512          rglDir[1] = dir->dir[1];
       
   513          rglDir[2] = dir->dir[2];
       
   514          return 0;
       
   515 
       
   516       default:
       
   517          SDL_SetError("Haptic: Unknown direction type.");
       
   518          return -1;
       
   519    }
       
   520 }
       
   521 
       
   522 #define CONVERT(x)   (((x)*10000) / 0xFFFF )
       
   523 /*
       
   524  * Creates the FFEFFECT from a SDL_HapticEffect.
       
   525  */
       
   526 static int
       
   527 SDL_SYS_ToFFEFFECT( SDL_Haptic * haptic, FFEFFECT * dest, SDL_HapticEffect * src )
       
   528 {
       
   529    int i;
       
   530    FFCONSTANTFORCE *constant;
       
   531    FFPERIODIC *periodic;
       
   532    FFCONDITION *condition; /* Actually an array of conditions - one per axis. */
       
   533    FFRAMPFORCE *ramp;
       
   534    FFCUSTOMFORCE *custom;
       
   535    FFENVELOPE *envelope;
       
   536    SDL_HapticConstant *hap_constant;
       
   537    SDL_HapticPeriodic *hap_periodic;
       
   538    SDL_HapticCondition *hap_condition;
       
   539    SDL_HapticRamp *hap_ramp;
       
   540    SDL_HapticCustom *hap_custom;
       
   541    DWORD *axes;
       
   542 
       
   543    /* Set global stuff. */
       
   544    SDL_memset(dest, 0, sizeof(FFEFFECT));
       
   545    dest->dwSize = sizeof(FFEFFECT); /* Set the structure size. */
       
   546    dest->dwSamplePeriod = 0; /* Not used by us. */
       
   547    dest->dwGain = 10000; /* Gain is set globally, not locally. */
       
   548 
       
   549    /* Envelope. */
       
   550    envelope = SDL_malloc( sizeof(FFENVELOPE) );
       
   551    if (envelope == NULL) {
       
   552       SDL_OutOfMemory();
       
   553       return -1;
       
   554    }
       
   555    SDL_memset(envelope, 0, sizeof(FFENVELOPE));
       
   556    dest->lpEnvelope = envelope;
       
   557    envelope->dwSize = sizeof(FFENVELOPE); /* Always should be this. */
       
   558 
       
   559    /* Axes. */
       
   560    dest->cAxes = haptic->naxes;
       
   561    if (dest->cAxes > 0) {
       
   562       axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
       
   563       if (axes == NULL) {
       
   564          SDL_OutOfMemory();
       
   565          return -1;
       
   566       }
       
   567       axes[0] = FFJOFS_X; /* Always at least one axis. */
       
   568       if (dest->cAxes > 1) {
       
   569          axes[1] = FFJOFS_Y;
       
   570       }
       
   571       if (dest->cAxes > 2) {
       
   572          axes[2] = FFJOFS_Z;
       
   573       }
       
   574       dest->rgdwAxes = axes;
       
   575    }
       
   576 
       
   577 
       
   578    /* The big type handling switch, even bigger then linux's version. */
       
   579    switch (src->type) {
       
   580       case SDL_HAPTIC_CONSTANT:
       
   581          hap_constant = &src->constant;
       
   582          constant = SDL_malloc( sizeof(FFCONSTANTFORCE) );
       
   583          if (constant == NULL) {
       
   584             SDL_OutOfMemory();
       
   585             return -1;
       
   586          }
       
   587          SDL_memset(constant, 0, sizeof(FFCONSTANTFORCE));
       
   588 
       
   589          /* Specifics */
       
   590          constant->lMagnitude = CONVERT(hap_constant->level);
       
   591          dest->cbTypeSpecificParams = sizeof(FFCONSTANTFORCE); 
       
   592          dest->lpvTypeSpecificParams = constant;
       
   593 
       
   594          /* Generics */
       
   595          dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */
       
   596          dest->dwTriggerButton = FFJOFS_BUTTON(hap_constant->button);
       
   597          dest->dwTriggerRepeatInterval = hap_constant->interval;
       
   598          dest->dwStartDelay = hap_constant->delay * 1000; /* In microseconds. */
       
   599 
       
   600          /* Direction. */
       
   601          if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes) < 0) {
       
   602             return -1;
       
   603          }
       
   604          
       
   605          /* Envelope */
       
   606          envelope->dwAttackLevel = CONVERT(hap_constant->attack_level);
       
   607          envelope->dwAttackTime = hap_constant->attack_length * 1000;
       
   608          envelope->dwFadeLevel = CONVERT(hap_constant->fade_level);
       
   609          envelope->dwFadeTime = hap_constant->fade_length * 1000;
       
   610 
       
   611          break;
       
   612 
       
   613       case SDL_HAPTIC_SINE:
       
   614       case SDL_HAPTIC_SQUARE:
       
   615       case SDL_HAPTIC_TRIANGLE:
       
   616       case SDL_HAPTIC_SAWTOOTHUP:
       
   617       case SDL_HAPTIC_SAWTOOTHDOWN:
       
   618          hap_periodic = &src->periodic;
       
   619          periodic = SDL_malloc(sizeof(FFPERIODIC));
       
   620          if (periodic == NULL) {
       
   621             SDL_OutOfMemory();
       
   622             return -1;
       
   623          }
       
   624          SDL_memset(periodic, 0, sizeof(FFPERIODIC));
       
   625 
       
   626          /* Specifics */
       
   627          periodic->dwMagnitude = CONVERT(hap_periodic->magnitude);
       
   628          periodic->lOffset = CONVERT(hap_periodic->offset);
       
   629          periodic->dwPhase = hap_periodic->phase;
       
   630          periodic->dwPeriod = hap_periodic->period * 1000;
       
   631          dest->cbTypeSpecificParams = sizeof(FFPERIODIC);
       
   632          dest->lpvTypeSpecificParams = periodic;
       
   633 
       
   634          /* Generics */
       
   635          dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */
       
   636          dest->dwTriggerButton = FFJOFS_BUTTON(hap_periodic->button);
       
   637          dest->dwTriggerRepeatInterval = hap_periodic->interval;
       
   638          dest->dwStartDelay = hap_periodic->delay * 1000; /* In microseconds. */
       
   639          
       
   640          /* Direction. */
       
   641          if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes) < 0) {
       
   642             return -1;
       
   643          }
       
   644          
       
   645          /* Envelope */
       
   646          envelope->dwAttackLevel = CONVERT(hap_periodic->attack_level);
       
   647          envelope->dwAttackTime = hap_periodic->attack_length * 1000;
       
   648          envelope->dwFadeLevel = CONVERT(hap_periodic->fade_level);
       
   649          envelope->dwFadeTime = hap_periodic->fade_length * 1000;
       
   650 
       
   651          break;
       
   652 
       
   653       case SDL_HAPTIC_SPRING:
       
   654       case SDL_HAPTIC_DAMPER:
       
   655       case SDL_HAPTIC_INERTIA:
       
   656       case SDL_HAPTIC_FRICTION:
       
   657          hap_condition = &src->condition;
       
   658          condition = SDL_malloc(sizeof(FFCONDITION) * dest->cAxes);
       
   659          if (condition == NULL) {
       
   660             SDL_OutOfMemory();
       
   661             return -1;
       
   662          }
       
   663          SDL_memset(condition, 0, sizeof(FFCONDITION));
       
   664 
       
   665          /* Specifics */
       
   666          for (i=0; i<dest->cAxes; i++) {
       
   667             condition[i].lOffset = CONVERT(hap_condition->center[i]);
       
   668             condition[i].lPositiveCoefficient = CONVERT(hap_condition->right_coeff[i]);
       
   669             condition[i].lNegativeCoefficient = CONVERT(hap_condition->left_coeff[i]);
       
   670             condition[i].dwPositiveSaturation = CONVERT(hap_condition->right_sat[i]);
       
   671             condition[i].dwNegativeSaturation = CONVERT(hap_condition->left_sat[i]);
       
   672             condition[i].lDeadBand = CONVERT(hap_condition->deadband[i]);
       
   673          }
       
   674          dest->cbTypeSpecificParams = sizeof(FFCONDITION) * dest->cAxes;
       
   675          dest->lpvTypeSpecificParams = condition;
       
   676 
       
   677          /* Generics */
       
   678          dest->dwDuration = hap_condition->length * 1000; /* In microseconds. */
       
   679          dest->dwTriggerButton = FFJOFS_BUTTON(hap_condition->button);
       
   680          dest->dwTriggerRepeatInterval = hap_condition->interval;
       
   681          dest->dwStartDelay = hap_condition->delay * 1000; /* In microseconds. */
       
   682 
       
   683          /* Direction. */
       
   684          if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes) < 0) {
       
   685             return -1;                
       
   686          }                            
       
   687                                       
       
   688          /* Envelope */
       
   689 /* TODO Check is envelope actually used.
       
   690          envelope->dwAttackLevel = CONVERT(hap_condition->attack_level);
       
   691          envelope->dwAttackTime = hap_condition->attack_length * 1000;
       
   692          envelope->dwFadeLevel = CONVERT(hap_condition->fade_level);
       
   693          envelope->dwFadeTime = hap_condition->fade_length * 1000;
       
   694 */
       
   695 
       
   696          break;
       
   697 
       
   698       case SDL_HAPTIC_RAMP:
       
   699          hap_ramp = &src->ramp;
       
   700          ramp = SDL_malloc(sizeof(FFRAMPFORCE));
       
   701          if (ramp == NULL) {
       
   702             SDL_OutOfMemory();
       
   703             return -1;
       
   704          }
       
   705          SDL_memset(ramp, 0, sizeof(FFRAMPFORCE));
       
   706 
       
   707          /* Specifics */
       
   708          ramp->lStart = CONVERT(hap_ramp->start);
       
   709          ramp->lEnd = CONVERT(hap_ramp->end);
       
   710          dest->cbTypeSpecificParams = sizeof(FFRAMPFORCE);
       
   711          dest->lpvTypeSpecificParams = ramp;
       
   712 
       
   713          /* Generics */
       
   714          dest->dwDuration = hap_ramp->length * 1000; /* In microseconds. */
       
   715          dest->dwTriggerButton = FFJOFS_BUTTON(hap_ramp->button);
       
   716          dest->dwTriggerRepeatInterval = hap_ramp->interval;
       
   717          dest->dwStartDelay = hap_ramp->delay * 1000; /* In microseconds. */
       
   718 
       
   719          /* Direction. */
       
   720          if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) {
       
   721             return -1;
       
   722          }
       
   723 
       
   724          /* Envelope */
       
   725          envelope->dwAttackLevel = CONVERT(hap_ramp->attack_level);
       
   726          envelope->dwAttackTime = hap_ramp->attack_length * 1000;
       
   727          envelope->dwFadeLevel = CONVERT(hap_ramp->fade_level);
       
   728          envelope->dwFadeTime = hap_ramp->fade_length * 1000;
       
   729 
       
   730          break;
       
   731 
       
   732       case SDL_HAPTIC_CUSTOM:
       
   733          hap_custom = &src->custom;
       
   734          custom = SDL_malloc(sizeof(FFCUSTOMFORCE));
       
   735          if (custom == NULL) {
       
   736             SDL_OutOfMemory();
       
   737             return -1;
       
   738          }
       
   739          SDL_memset(custom, 0, sizeof(FFCUSTOMFORCE));
       
   740 
       
   741          /* Specifics */
       
   742          custom->cChannels = hap_custom->channels;
       
   743          custom->dwSamplePeriod = hap_custom->period * 1000;
       
   744          custom->cSamples = hap_custom->samples;
       
   745          custom->rglForceData = SDL_malloc(sizeof(LONG)*custom->cSamples*custom->cChannels);
       
   746          for (i=0; i<hap_custom->samples*hap_custom->channels; i++) { /* Copy data. */
       
   747             custom->rglForceData[i] = CONVERT(hap_custom->data[i]);
       
   748          }
       
   749          dest->cbTypeSpecificParams = sizeof(FFCUSTOMFORCE);
       
   750          dest->lpvTypeSpecificParams = custom;
       
   751 
       
   752          /* Generics */
       
   753          dest->dwDuration = hap_custom->length * 1000; /* In microseconds. */
       
   754          dest->dwTriggerButton = FFJOFS_BUTTON(hap_custom->button);
       
   755          dest->dwTriggerRepeatInterval = hap_custom->interval;
       
   756          dest->dwStartDelay = hap_custom->delay * 1000; /* In microseconds. */
       
   757 
       
   758          /* Direction. */
       
   759          if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) < 0) {
       
   760             return -1;
       
   761          }
       
   762          
       
   763          /* Envelope */
       
   764          envelope->dwAttackLevel = CONVERT(hap_custom->attack_level);
       
   765          envelope->dwAttackTime = hap_custom->attack_length * 1000;
       
   766          envelope->dwFadeLevel = CONVERT(hap_custom->fade_level);
       
   767          envelope->dwFadeTime = hap_custom->fade_length * 1000;
       
   768 
       
   769          break;
       
   770 
       
   771 
       
   772       default:
       
   773          SDL_SetError("Haptic: Unknown effect type.");
       
   774          return -1;
       
   775    }
       
   776 
       
   777    return 0;
       
   778 }
       
   779 
       
   780 
       
   781 /*
       
   782  * Frees an FFEFFECT allocated by SDL_SYS_ToFFEFFECT.
       
   783  */
       
   784 static void
       
   785 SDL_SYS_HapticFreeFFEFFECT( FFEFFECT * effect, int type )
       
   786 {
       
   787    FFCUSTOMFORCE *custom;
       
   788 
       
   789    if (effect->lpEnvelope != NULL) {
       
   790       SDL_free(effect->lpEnvelope);
       
   791       effect->lpEnvelope = NULL;
       
   792    }
       
   793    if (effect->rgdwAxes != NULL) {
       
   794       SDL_free(effect->rgdwAxes);
       
   795       effect->rgdwAxes = NULL;
       
   796    }
       
   797    if (effect->lpvTypeSpecificParams != NULL) {
       
   798       if (type == SDL_HAPTIC_CUSTOM) { /* Must free the custom data. */
       
   799          custom = (FFCUSTOMFORCE*) effect->lpvTypeSpecificParams;
       
   800          SDL_free(custom->rglForceData);
       
   801          custom->rglForceData = NULL;
       
   802       }
       
   803       SDL_free(effect->lpvTypeSpecificParams);
       
   804       effect->lpvTypeSpecificParams = NULL;
       
   805    }
       
   806    if (effect->rglDirection != NULL) {
       
   807       SDL_free(effect->rglDirection);
       
   808       effect->rglDirection = NULL;
       
   809    }
       
   810 }
       
   811 
       
   812 
       
   813 /*
       
   814  * Gets the effect type from the generic SDL haptic effect wrapper.
       
   815  */
       
   816 REFGUID
       
   817 SDL_SYS_HapticEffectType(struct haptic_effect * effect)
       
   818 {
       
   819    switch (effect->effect.type) {
       
   820       case SDL_HAPTIC_CONSTANT:
       
   821          return GUID_ConstantForce;
       
   822 
       
   823       case SDL_HAPTIC_RAMP:
       
   824          return GUID_RampForce;
       
   825 
       
   826       case SDL_HAPTIC_SQUARE:
       
   827          return GUID_Square;
       
   828 
       
   829       case SDL_HAPTIC_SINE:
       
   830          return GUID_Sine;
       
   831 
       
   832       case SDL_HAPTIC_TRIANGLE:
       
   833          return GUID_Triangle;
       
   834 
       
   835       case SDL_HAPTIC_SAWTOOTHUP:
       
   836          return GUID_SawtoothUp;
       
   837 
       
   838       case SDL_HAPTIC_SAWTOOTHDOWN:
       
   839          return GUID_SawtoothDown;
       
   840 
       
   841       case SDL_HAPTIC_SPRING:
       
   842          return GUID_Spring;
       
   843 
       
   844       case SDL_HAPTIC_DAMPER:
       
   845          return GUID_Damper;
       
   846 
       
   847       case SDL_HAPTIC_INERTIA:
       
   848          return GUID_Inertia;
       
   849 
       
   850       case SDL_HAPTIC_FRICTION:
       
   851          return GUID_Friction;
       
   852 
       
   853       case SDL_HAPTIC_CUSTOM:
       
   854          return GUID_CustomForce;
       
   855 
       
   856       default:
       
   857          SDL_SetError("Haptic: Unknown effect type.");
       
   858          return NULL;
       
   859    }
       
   860 }
       
   861 
       
   862 
       
   863 /*
       
   864  * Creates a new haptic effect.
       
   865  */
       
   866 int
       
   867 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect * effect,
       
   868       SDL_HapticEffect * base)
       
   869 {
       
   870    HRESULT ret;
       
   871    REFGUID type;
       
   872 
       
   873    /* Alloc the effect. */
       
   874    effect->hweffect = (struct haptic_hweffect *)
       
   875          SDL_malloc(sizeof(struct haptic_hweffect));
       
   876    if (effect->hweffect == NULL) {
       
   877       SDL_OutOfMemory();
       
   878       goto err_hweffect;
       
   879    }
       
   880 
       
   881    /* Get the type. */
       
   882    type = SDL_SYS_HapticEffectType(effect);
       
   883    if (type == NULL) {
       
   884       goto err_hweffect;
       
   885    }
       
   886 
       
   887    /* Get the effect. */
       
   888    if (SDL_SYS_ToFFEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
       
   889       goto err_effectdone;
       
   890    }
       
   891 
       
   892    /* Create the actual effect. */
       
   893    ret = FFDeviceCreateEffect(haptic->hwdata->device, type,
       
   894          &effect->hweffect->effect, &effect->hweffect->ref);
       
   895    if (ret != FF_OK) {
       
   896       SDL_SetError("Haptic: Unable to create effect: %s.", FFStrError(ret));
       
   897       goto err_effectdone;
       
   898    }
       
   899 
       
   900    return 0;
       
   901 
       
   902 err_effectdone:
       
   903    SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, base->type);
       
   904 err_hweffect:
       
   905    if (effect->hweffect != NULL) {
       
   906       SDL_free(effect->hweffect);
       
   907       effect->hweffect = NULL;
       
   908    }
       
   909    return -1;
       
   910 }
       
   911 
       
   912 
       
   913 /*
       
   914  * Updates an effect.
       
   915  */
       
   916 int
       
   917 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
       
   918       struct haptic_effect * effect, SDL_HapticEffect * data)
       
   919 {
       
   920    HRESULT ret;
       
   921    FFEffectParameterFlag flags;
       
   922    FFEFFECT temp;
       
   923 
       
   924    /* Get the effect. */
       
   925    SDL_memset(&temp, 0, sizeof(FFEFFECT));
       
   926    if (SDL_SYS_ToFFEFFECT(haptic, &temp, data) < 0) {
       
   927       goto err_update;
       
   928    }
       
   929 
       
   930    /* Set the flags.  Might be worthwhile to diff temp with loaded effect and
       
   931     *  only change those parameters. */
       
   932    flags = FFEP_ALLPARAMS;
       
   933 
       
   934    /* Create the actual effect. */
       
   935    ret = FFEffectSetParameters(effect->hweffect->ref, &temp, flags);
       
   936    if (ret != FF_OK) {
       
   937       SDL_SetError("Haptic: Unable to update effect: %s.", FFStrError(ret));
       
   938       goto err_update;
       
   939    }
       
   940 
       
   941    /* Copy it over. */
       
   942    SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, data->type);
       
   943    SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(FFEFFECT));
       
   944 
       
   945    return 0;
       
   946 
       
   947 err_update:
       
   948    SDL_SYS_HapticFreeFFEFFECT(&temp, data->type);
       
   949    return -1;
       
   950 }
       
   951 
       
   952 
       
   953 /*
       
   954  * Runs an effect.
       
   955  */
       
   956 int
       
   957 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect * effect,
       
   958                         Uint32 iterations)
       
   959 {
       
   960    HRESULT ret;
       
   961    Uint32 iter;
       
   962 
       
   963    /* Check if it's infinite. */
       
   964    if (iterations == SDL_HAPTIC_INFINITY) {
       
   965       iter = FF_INFINITE;
       
   966    }
       
   967    else
       
   968       iter = iterations;
       
   969 
       
   970    /* Run the effect. */
       
   971    ret = FFEffectStart(effect->hweffect->ref, iter, 0);
       
   972    if (ret != FF_OK) {
       
   973       SDL_SetError("Haptic: Unable to run the effect: %s.", FFStrError(ret));
       
   974       return -1;
       
   975    }
       
   976 
       
   977    return 0;
       
   978 }
       
   979 
       
   980 
       
   981 /*
       
   982  * Stops an effect.
       
   983  */
       
   984 int
       
   985 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect * effect)
       
   986 {
       
   987    HRESULT ret;
       
   988 
       
   989    ret = FFEffectStop(effect->hweffect->ref);
       
   990    if (ret != FF_OK) {
       
   991       SDL_SetError("Haptic: Unable to stop the effect: %s.", FFStrError(ret));
       
   992       return -1;
       
   993    }
       
   994 
       
   995    return 0;
       
   996 }
       
   997 
       
   998 
       
   999 /*
       
  1000  * Frees the effect.
       
  1001  */
       
  1002 void
       
  1003 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect * effect)
       
  1004 {
       
  1005    HRESULT ret;
       
  1006 
       
  1007    ret = FFDeviceReleaseEffect(haptic->hwdata->device, effect->hweffect->ref);
       
  1008    if (ret != FF_OK) {
       
  1009       SDL_SetError("Haptic: Error removing the effect from the device: %s.",
       
  1010                    FFStrError(ret));
       
  1011    }
       
  1012    SDL_free(effect->hweffect->effect.lpvTypeSpecificParams);
       
  1013    effect->hweffect->effect.lpvTypeSpecificParams = NULL;
       
  1014    SDL_free(effect->hweffect);
       
  1015    effect->hweffect = NULL;
       
  1016 }
       
  1017 
       
  1018 
       
  1019 /*
       
  1020  * Gets the status of a haptic effect.
       
  1021  */
       
  1022 int
       
  1023 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect * effect)
       
  1024 {
       
  1025 }
       
  1026 
       
  1027 
       
  1028 /*
       
  1029  * Sets the gain.
       
  1030  */
       
  1031 int
       
  1032 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
       
  1033 {
       
  1034    HRESULT ret;
       
  1035    DIPROPDWORD dipdw;
       
  1036 
       
  1037    /* Create the weird structure thingy. */
       
  1038    dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
       
  1039    dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
       
  1040    dipdw.diph.dwObj        = 0;
       
  1041    dipdw.diph.dwHow        = DIPH_DEVICE;
       
  1042    dipdw.dwData            = gain * 100; /* 0 to 10,000 */
       
  1043 
       
  1044    /* Try to set the autocenter. */
       
  1045    ret = IDirectInputDevice2_SetProperty( haptic->hwdata->device,
       
  1046                                           DIPROP_FFGAIN, &dipdw.diph );
       
  1047    if (FAILED(ret)) {
       
  1048       DI_SetError("Setting gain",ret);
       
  1049       return -1;
       
  1050    }
       
  1051   
       
  1052    return 0;
       
  1053 }
       
  1054 
       
  1055 
       
  1056 /*
       
  1057  * Sets the autocentering.
       
  1058  */
       
  1059 int
       
  1060 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
       
  1061 {
       
  1062    HRESULT ret;
       
  1063    DIPROPDWORD dipdw;
       
  1064 
       
  1065    /* Create the weird structure thingy. */
       
  1066    dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
       
  1067    dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
       
  1068    dipdw.diph.dwObj        = 0;
       
  1069    dipdw.diph.dwHow        = DIPH_DEVICE;
       
  1070    dipdw.dwData            = (autocenter == 0) ? DIPROPAUTOCENTER_OFF : 
       
  1071                                                  DIPROPAUTOCENTER_ON;
       
  1072 
       
  1073    /* Try to set the autocenter. */
       
  1074    ret = IDirectInputDevice2_SetProperty( haptic->hwdata->device,
       
  1075                                           DIPROP_AUTOCENTER, &dipdw.diph );
       
  1076    if (FAILED(ret)) {
       
  1077       DI_SetError("Setting autocenter",ret);
       
  1078       return -1;
       
  1079    }
       
  1080   
       
  1081    return 0;
       
  1082 
       
  1083 }
       
  1084 
       
  1085 
       
  1086 #endif /* SDL_HAPTIC_DINPUT */