src/haptic/windows/SDL_syshaptic.c
changeset 6990 2514368c2aaf
parent 6885 700f1b25f77f
child 7037 3fedf1f25b94
     1.1 --- a/src/haptic/windows/SDL_syshaptic.c	Sun Mar 10 09:09:31 2013 -0700
     1.2 +++ b/src/haptic/windows/SDL_syshaptic.c	Sun Mar 10 13:05:47 2013 -0400
     1.3 @@ -22,16 +22,16 @@
     1.4  
     1.5  #ifdef SDL_HAPTIC_DINPUT
     1.6  
     1.7 +#include "SDL_assert.h"
     1.8 +#include "SDL_hints.h"
     1.9  #include "SDL_haptic.h"
    1.10  #include "../SDL_syshaptic.h"
    1.11  #include "SDL_joystick.h"
    1.12  #include "../../joystick/SDL_sysjoystick.h"     /* For the real SDL_Joystick */
    1.13  #include "../../joystick/windows/SDL_dxjoystick_c.h"      /* For joystick hwdata */
    1.14  
    1.15 -
    1.16  #define MAX_HAPTICS  32
    1.17  
    1.18 -
    1.19  /*
    1.20   * List of available haptic devices.
    1.21   */
    1.22 @@ -41,6 +41,8 @@
    1.23      char *name;
    1.24      SDL_Haptic *haptic;
    1.25      DIDEVCAPS capabilities;
    1.26 +    Uint8 bXInputHaptic; // Supports force feedback via XInput.
    1.27 +    Uint8 userid; // XInput userid index for this joystick
    1.28  } SDL_hapticlist[MAX_HAPTICS];
    1.29  
    1.30  
    1.31 @@ -52,6 +54,8 @@
    1.32      LPDIRECTINPUTDEVICE8 device;
    1.33      DWORD axes[3];              /* Axes to use. */
    1.34      int is_joystick;            /* Device is loaded as joystick. */
    1.35 +    Uint8 bXInputHaptic; // Supports force feedback via XInput.
    1.36 +    Uint8 userid; // XInput userid index for this joystick
    1.37  };
    1.38  
    1.39  
    1.40 @@ -62,6 +66,7 @@
    1.41  {
    1.42      DIEFFECT effect;
    1.43      LPDIRECTINPUTEFFECT ref;
    1.44 +    XINPUT_VIBRATION vibration;
    1.45  };
    1.46  
    1.47  
    1.48 @@ -70,6 +75,7 @@
    1.49   */
    1.50  static SDL_bool coinitialized = SDL_FALSE;
    1.51  static LPDIRECTINPUT8 dinput = NULL;
    1.52 +static SDL_bool loaded_xinput = SDL_FALSE;
    1.53  
    1.54  
    1.55  /*
    1.56 @@ -87,6 +93,7 @@
    1.57                                            DIDEVICEINSTANCE instance);
    1.58  static int SDL_SYS_HapticOpenFromDevice8(SDL_Haptic * haptic,
    1.59                                           LPDIRECTINPUTDEVICE8 device8);
    1.60 +static int SDL_SYS_HapticOpenFromXInput(SDL_Haptic * haptic, Uint8 userid);
    1.61  static DWORD DIGetTriggerButton(Uint16 button);
    1.62  static int SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir,
    1.63                                  int naxes);
    1.64 @@ -130,6 +137,7 @@
    1.65  int
    1.66  SDL_SYS_HapticInit(void)
    1.67  {
    1.68 +    const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED);
    1.69      HRESULT ret;
    1.70      HINSTANCE instance;
    1.71  
    1.72 @@ -187,6 +195,30 @@
    1.73          return -1;
    1.74      }
    1.75  
    1.76 +    if (!env || SDL_atoi(env)) {
    1.77 +        loaded_xinput = (WIN_LoadXInputDLL() == 0);
    1.78 +    }
    1.79 +
    1.80 +    if (loaded_xinput) {
    1.81 +        DWORD i;
    1.82 +        const SDL_bool bIs14OrLater = (SDL_XInputVersion >= ((1<<16)|4));
    1.83 +
    1.84 +        for (i = 0; (i < SDL_XINPUT_MAX_DEVICES) && (SDL_numhaptics < MAX_HAPTICS); i++) {
    1.85 +            XINPUT_CAPABILITIES caps;
    1.86 +            if (XINPUTGETCAPABILITIES(i, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) {
    1.87 +                if ((!bIs14OrLater) || (caps.Flags & XINPUT_CAPS_FFB_SUPPORTED)) {
    1.88 +                    /* !!! FIXME: I'm not bothering to query for a real name right now. */
    1.89 +                    char buf[64];
    1.90 +                    SDL_snprintf(buf, sizeof (buf), "XInput Controller #%u", i+1);
    1.91 +                    SDL_hapticlist[SDL_numhaptics].name = SDL_strdup(buf);
    1.92 +                    SDL_hapticlist[SDL_numhaptics].bXInputHaptic = 1;
    1.93 +                    SDL_hapticlist[SDL_numhaptics].userid = (Uint8) i;
    1.94 +                    SDL_numhaptics++;
    1.95 +                }
    1.96 +            }
    1.97 +        }
    1.98 +    }
    1.99 +
   1.100      return SDL_numhaptics;
   1.101  }
   1.102  
   1.103 @@ -363,6 +395,43 @@
   1.104      return -1;
   1.105  }
   1.106  
   1.107 +static int
   1.108 +SDL_SYS_HapticOpenFromXInput(SDL_Haptic * haptic, Uint8 userid)
   1.109 +{
   1.110 +    XINPUT_VIBRATION vibration = { 0, 0 };  /* stop any current vibration */
   1.111 +    XINPUTSETSTATE(userid, &vibration);
   1.112 +
   1.113 +    /* !!! FIXME: we can probably do more than SINE if we figure out how to set up the left and right motors properly. */
   1.114 +    haptic->supported = SDL_HAPTIC_SINE;
   1.115 +
   1.116 +    haptic->neffects = 1;
   1.117 +    haptic->nplaying = 1;
   1.118 +
   1.119 +    /* Prepare effects memory. */
   1.120 +    haptic->effects = (struct haptic_effect *)
   1.121 +        SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
   1.122 +    if (haptic->effects == NULL) {
   1.123 +        SDL_OutOfMemory();
   1.124 +        return -1;
   1.125 +    }
   1.126 +    /* Clear the memory */
   1.127 +    SDL_memset(haptic->effects, 0,
   1.128 +               sizeof(struct haptic_effect) * haptic->neffects);
   1.129 +
   1.130 +    haptic->hwdata = (struct haptic_hwdata *) SDL_malloc(sizeof(*haptic->hwdata));
   1.131 +    if (haptic->hwdata == NULL) {
   1.132 +        SDL_free(haptic->effects);
   1.133 +        haptic->effects = NULL;
   1.134 +        SDL_OutOfMemory();
   1.135 +        return -1;
   1.136 +    }
   1.137 +    SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
   1.138 +
   1.139 +    haptic->hwdata->bXInputHaptic = 1;
   1.140 +    haptic->hwdata->userid = userid;
   1.141 +
   1.142 +    return 0;
   1.143 + }
   1.144  
   1.145  /*
   1.146   * Opens the haptic device from the file descriptor.
   1.147 @@ -504,9 +573,11 @@
   1.148  int
   1.149  SDL_SYS_HapticOpen(SDL_Haptic * haptic)
   1.150  {
   1.151 -    return SDL_SYS_HapticOpenFromInstance(haptic,
   1.152 -                                          SDL_hapticlist[haptic->index].
   1.153 -                                          instance);
   1.154 +    if (SDL_hapticlist[haptic->index].bXInputHaptic) {
   1.155 +        return SDL_SYS_HapticOpenFromXInput(haptic, SDL_hapticlist[haptic->index].userid);
   1.156 +    }
   1.157 +
   1.158 +    return SDL_SYS_HapticOpenFromInstance(haptic, SDL_hapticlist[haptic->index].instance);
   1.159  }
   1.160  
   1.161  
   1.162 @@ -535,11 +606,9 @@
   1.163  int
   1.164  SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
   1.165  {
   1.166 -    if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
   1.167 -        return SDL_TRUE;
   1.168 -    }
   1.169 -
   1.170 -    return SDL_FALSE;
   1.171 +    const struct joystick_hwdata *hwdata = joystick->hwdata;
   1.172 +    return ( (hwdata->bXInputHaptic) ||
   1.173 +             ((hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) != 0) );
   1.174  }
   1.175  
   1.176  
   1.177 @@ -549,25 +618,30 @@
   1.178  int
   1.179  SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
   1.180  {
   1.181 -    HRESULT ret;
   1.182 -    DIDEVICEINSTANCE hap_instance, joy_instance;
   1.183 -    hap_instance.dwSize = sizeof(DIDEVICEINSTANCE);
   1.184 -    joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
   1.185 +    if ((joystick->hwdata->bXInputHaptic == haptic->hwdata->bXInputHaptic) && (haptic->hwdata->userid == joystick->hwdata->userid)) {
   1.186 +        return 1;
   1.187 +    } else {
   1.188 +        HRESULT ret;
   1.189 +        DIDEVICEINSTANCE hap_instance, joy_instance;
   1.190  
   1.191 -    /* Get the device instances. */
   1.192 -    ret = IDirectInputDevice8_GetDeviceInfo(haptic->hwdata->device,
   1.193 +        hap_instance.dwSize = sizeof(DIDEVICEINSTANCE);
   1.194 +        joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
   1.195 +
   1.196 +        /* Get the device instances. */
   1.197 +        ret = IDirectInputDevice8_GetDeviceInfo(haptic->hwdata->device,
   1.198                                              &hap_instance);
   1.199 -    if (FAILED(ret)) {
   1.200 -        return 0;
   1.201 +        if (FAILED(ret)) {
   1.202 +            return 0;
   1.203 +        }
   1.204 +        ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice,
   1.205 +                                                &joy_instance);
   1.206 +        if (FAILED(ret)) {
   1.207 +            return 0;
   1.208 +        }
   1.209 +
   1.210 +        if (DI_GUIDIsSame(&hap_instance.guidInstance, &joy_instance.guidInstance))
   1.211 +            return 1;
   1.212      }
   1.213 -    ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice,
   1.214 -                                            &joy_instance);
   1.215 -    if (FAILED(ret)) {
   1.216 -        return 0;
   1.217 -    }
   1.218 -
   1.219 -    if (DI_GUIDIsSame(&hap_instance.guidInstance, &joy_instance.guidInstance))
   1.220 -        return 1;
   1.221  
   1.222      return 0;
   1.223  }
   1.224 @@ -585,16 +659,27 @@
   1.225      joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
   1.226  
   1.227      /* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */
   1.228 -    for (i=0; i<SDL_numhaptics; i++) {
   1.229 -        idret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice,
   1.230 -              &joy_instance);
   1.231 -        if (FAILED(idret)) {
   1.232 -            return -1;
   1.233 +    if (joystick->hwdata->bXInputDevice) {
   1.234 +        const Uint8 userid = joystick->hwdata->userid;
   1.235 +        for (i=0; i<SDL_numhaptics; i++) {
   1.236 +            if ((SDL_hapticlist[i].bXInputHaptic) && (SDL_hapticlist[i].userid == userid)) {
   1.237 +                SDL_assert(joystick->hwdata->bXInputHaptic);
   1.238 +                haptic->index = i;
   1.239 +                break;
   1.240 +            }
   1.241          }
   1.242 -        if (DI_GUIDIsSame(&SDL_hapticlist[i].instance.guidInstance,
   1.243 -                          &joy_instance.guidInstance)) {
   1.244 -            haptic->index = i;
   1.245 -            break;
   1.246 +    } else {
   1.247 +        for (i=0; i<SDL_numhaptics; i++) {
   1.248 +            idret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice,
   1.249 +                  &joy_instance);
   1.250 +            if (FAILED(idret)) {
   1.251 +                return -1;
   1.252 +            }
   1.253 +            if (DI_GUIDIsSame(&SDL_hapticlist[i].instance.guidInstance,
   1.254 +                              &joy_instance.guidInstance)) {
   1.255 +                haptic->index = i;
   1.256 +                break;
   1.257 +            }
   1.258          }
   1.259      }
   1.260      if (i >= SDL_numhaptics) {
   1.261 @@ -611,14 +696,17 @@
   1.262      SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
   1.263  
   1.264      /* Now open the device. */
   1.265 -    ret =
   1.266 -        SDL_SYS_HapticOpenFromDevice8(haptic, joystick->hwdata->InputDevice);
   1.267 -    if (ret < 0) {
   1.268 -        return -1;
   1.269 +    if (!joystick->hwdata->bXInputHaptic) {
   1.270 +        ret = SDL_SYS_HapticOpenFromDevice8(haptic, joystick->hwdata->InputDevice);
   1.271 +        if (ret < 0) {
   1.272 +            return -1;
   1.273 +        }
   1.274      }
   1.275  
   1.276      /* It's using the joystick device. */
   1.277      haptic->hwdata->is_joystick = 1;
   1.278 +    haptic->hwdata->bXInputHaptic = joystick->hwdata->bXInputHaptic;
   1.279 +    haptic->hwdata->userid = joystick->hwdata->userid;
   1.280  
   1.281      return 0;
   1.282  }
   1.283 @@ -638,10 +726,12 @@
   1.284          haptic->neffects = 0;
   1.285  
   1.286          /* Clean up */
   1.287 -        IDirectInputDevice8_Unacquire(haptic->hwdata->device);
   1.288 -        /* Only release if isn't grabbed by a joystick. */
   1.289 -        if (haptic->hwdata->is_joystick == 0) {
   1.290 -            IDirectInputDevice8_Release(haptic->hwdata->device);
   1.291 +        if (!haptic->hwdata->bXInputHaptic) {
   1.292 +            IDirectInputDevice8_Unacquire(haptic->hwdata->device);
   1.293 +            /* Only release if isn't grabbed by a joystick. */
   1.294 +            if (haptic->hwdata->is_joystick == 0) {
   1.295 +                IDirectInputDevice8_Release(haptic->hwdata->device);
   1.296 +            }
   1.297          }
   1.298  
   1.299          /* Free */
   1.300 @@ -659,6 +749,11 @@
   1.301  {
   1.302      int i;
   1.303  
   1.304 +    if (loaded_xinput) {
   1.305 +        WIN_UnloadXInputDLL();
   1.306 +        loaded_xinput = SDL_FALSE;
   1.307 +    }
   1.308 +
   1.309      for (i = 0; i < SDL_arraysize(SDL_hapticlist); ++i) {
   1.310          if (SDL_hapticlist[i].name) {
   1.311              SDL_free(SDL_hapticlist[i].name);
   1.312 @@ -1127,9 +1222,8 @@
   1.313                          SDL_HapticEffect * base)
   1.314  {
   1.315      HRESULT ret;
   1.316 +    REFGUID type = SDL_SYS_HapticEffectType(base);
   1.317  
   1.318 -    /* Get the type. */
   1.319 -    REFGUID type = SDL_SYS_HapticEffectType(base);
   1.320      if (type == NULL) {
   1.321          goto err_hweffect;
   1.322      }
   1.323 @@ -1142,6 +1236,13 @@
   1.324          goto err_hweffect;
   1.325      }
   1.326  
   1.327 +    SDL_zerop(effect->hweffect);
   1.328 +
   1.329 +    if (haptic->hwdata->bXInputHaptic) {
   1.330 +        SDL_assert(base->type == SDL_HAPTIC_SINE);  /* should catch this at higher level */
   1.331 +        return SDL_SYS_HapticUpdateEffect(haptic, effect, base);
   1.332 +    }
   1.333 +
   1.334      /* Get the effect. */
   1.335      if (SDL_SYS_ToDIEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
   1.336          goto err_effectdone;
   1.337 @@ -1181,6 +1282,23 @@
   1.338      DWORD flags;
   1.339      DIEFFECT temp;
   1.340  
   1.341 +    if (haptic->hwdata->bXInputHaptic) {
   1.342 +        // !!! FIXME: this isn't close to right. We only support "sine" effects,
   1.343 +        // !!! FIXME:  we ignore most of the parameters, and we probably get
   1.344 +        // !!! FIXME:  the ones we don't ignore wrong, too.
   1.345 +        // !!! FIXME: if I had a better understanding of how the two motors
   1.346 +        // !!! FIXME:  could be used in unison, perhaps I could implement other
   1.347 +        // !!! FIXME:  effect types?
   1.348 +        /* From MSDN:
   1.349 +            "Note that the right motor is the high-frequency motor, the left
   1.350 +             motor is the low-frequency motor. They do not always need to be
   1.351 +             set to the same amount, as they provide different effects." */
   1.352 +        XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
   1.353 +        SDL_assert(data->type == SDL_HAPTIC_SINE);
   1.354 +        vib->wLeftMotorSpeed = vib->wRightMotorSpeed = data->periodic.magnitude * 2;
   1.355 +        return 0;
   1.356 +    }
   1.357 +
   1.358      /* Get the effect. */
   1.359      SDL_memset(&temp, 0, sizeof(DIEFFECT));
   1.360      if (SDL_SYS_ToDIEFFECT(haptic, &temp, data) < 0) {
   1.361 @@ -1226,6 +1344,11 @@
   1.362      HRESULT ret;
   1.363      DWORD iter;
   1.364  
   1.365 +    if (haptic->hwdata->bXInputHaptic) {
   1.366 +        XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
   1.367 +        return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS);
   1.368 +    }
   1.369 +
   1.370      /* Check if it's infinite. */
   1.371      if (iterations == SDL_HAPTIC_INFINITY) {
   1.372          iter = INFINITE;
   1.373 @@ -1251,6 +1374,11 @@
   1.374  {
   1.375      HRESULT ret;
   1.376  
   1.377 +    if (haptic->hwdata->bXInputHaptic) {
   1.378 +        XINPUT_VIBRATION vibration = { 0, 0 };
   1.379 +        return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS);
   1.380 +    }
   1.381 +
   1.382      ret = IDirectInputEffect_Stop(effect->hweffect->ref);
   1.383      if (FAILED(ret)) {
   1.384          DI_SetError("Unable to stop effect", ret);
   1.385 @@ -1269,12 +1397,16 @@
   1.386  {
   1.387      HRESULT ret;
   1.388  
   1.389 -    ret = IDirectInputEffect_Unload(effect->hweffect->ref);
   1.390 -    if (FAILED(ret)) {
   1.391 -        DI_SetError("Removing effect from the device", ret);
   1.392 +    if (haptic->hwdata->bXInputHaptic) {
   1.393 +        SDL_SYS_HapticStopEffect(haptic, effect);
   1.394 +    } else {
   1.395 +        ret = IDirectInputEffect_Unload(effect->hweffect->ref);
   1.396 +        if (FAILED(ret)) {
   1.397 +            DI_SetError("Removing effect from the device", ret);
   1.398 +        }
   1.399 +        SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect,
   1.400 +                                   effect->effect.type);
   1.401      }
   1.402 -    SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect,
   1.403 -                               effect->effect.type);
   1.404      SDL_free(effect->hweffect);
   1.405      effect->hweffect = NULL;
   1.406  }
   1.407 @@ -1407,6 +1539,11 @@
   1.408  {
   1.409      HRESULT ret;
   1.410  
   1.411 +    if (haptic->hwdata->bXInputHaptic) {
   1.412 +        XINPUT_VIBRATION vibration = { 0, 0 };
   1.413 +        return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS);
   1.414 +    }
   1.415 +
   1.416      /* Try to stop the effects. */
   1.417      ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
   1.418                                                         DISFFC_STOPALL);