src/haptic/windows/SDL_syshaptic.c
author Ryan C. Gordon <icculus@icculus.org>
Tue, 18 Mar 2014 17:16:28 -0400
changeset 8634 a60eaa04d3fb
parent 8629 4c01875a4620
child 8688 1543a7183f88
permissions -rw-r--r--
Fixed SDL_HapticNewEffect() failing on various DirectInput devices.
slouken@2713
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@8149
     3
  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
slouken@2713
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@2713
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@2713
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@2713
    20
*/
icculus@8093
    21
#include "../../SDL_internal.h"
slouken@2713
    22
slouken@2713
    23
#ifdef SDL_HAPTIC_DINPUT
slouken@2713
    24
icculus@6990
    25
#include "SDL_assert.h"
icculus@7481
    26
#include "SDL_thread.h"
icculus@7481
    27
#include "SDL_mutex.h"
icculus@7481
    28
#include "SDL_timer.h"
icculus@6990
    29
#include "SDL_hints.h"
slouken@2713
    30
#include "SDL_haptic.h"
slouken@2713
    31
#include "../SDL_syshaptic.h"
slouken@2713
    32
#include "SDL_joystick.h"
slouken@2713
    33
#include "../../joystick/SDL_sysjoystick.h"     /* For the real SDL_Joystick */
slouken@5062
    34
#include "../../joystick/windows/SDL_dxjoystick_c.h"      /* For joystick hwdata */
slouken@2713
    35
icculus@8179
    36
#include "SDL_syshaptic_c.h"
slouken@2713
    37
slouken@2713
    38
/*
slouken@2713
    39
 * List of available haptic devices.
slouken@2713
    40
 */
icculus@8179
    41
typedef struct SDL_hapticlist_item
slouken@2713
    42
{
slouken@2713
    43
    DIDEVICEINSTANCE instance;
slouken@5090
    44
    char *name;
slouken@2713
    45
    SDL_Haptic *haptic;
slouken@2713
    46
    DIDEVCAPS capabilities;
slouken@7191
    47
    Uint8 bXInputHaptic; /* Supports force feedback via XInput. */
slouken@7191
    48
    Uint8 userid; /* XInput userid index for this joystick */
icculus@8179
    49
    struct SDL_hapticlist_item *next;
icculus@8179
    50
} SDL_hapticlist_item;
slouken@2713
    51
slouken@2713
    52
slouken@2713
    53
/*
slouken@2713
    54
 * Haptic system hardware data.
slouken@2713
    55
 */
slouken@2713
    56
struct haptic_hwdata
slouken@2713
    57
{
icculus@6716
    58
    LPDIRECTINPUTDEVICE8 device;
slouken@2713
    59
    DWORD axes[3];              /* Axes to use. */
slouken@7319
    60
    SDL_bool is_joystick;       /* Device is loaded as joystick. */
slouken@7191
    61
    Uint8 bXInputHaptic; /* Supports force feedback via XInput. */
slouken@7191
    62
    Uint8 userid; /* XInput userid index for this joystick */
icculus@7481
    63
    SDL_Thread *thread;
icculus@7481
    64
    SDL_mutex *mutex;
icculus@7481
    65
    volatile Uint32 stopTicks;
icculus@7481
    66
    volatile int stopThread;
slouken@2713
    67
};
slouken@2713
    68
slouken@2713
    69
slouken@2713
    70
/*
slouken@2713
    71
 * Haptic system effect data.
slouken@2713
    72
 */
slouken@2713
    73
struct haptic_hweffect
slouken@2713
    74
{
slouken@2713
    75
    DIEFFECT effect;
slouken@2713
    76
    LPDIRECTINPUTEFFECT ref;
icculus@6990
    77
    XINPUT_VIBRATION vibration;
slouken@2713
    78
};
slouken@2713
    79
slouken@2713
    80
slouken@2713
    81
/*
slouken@2713
    82
 * Internal stuff.
slouken@2713
    83
 */
icculus@5591
    84
static SDL_bool coinitialized = SDL_FALSE;
icculus@6716
    85
static LPDIRECTINPUT8 dinput = NULL;
icculus@6990
    86
static SDL_bool loaded_xinput = SDL_FALSE;
icculus@8179
    87
static SDL_hapticlist_item *SDL_hapticlist = NULL;
icculus@8179
    88
static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
icculus@8179
    89
static int numhaptics = 0;
slouken@2713
    90
slouken@2713
    91
/*
slouken@2713
    92
 * External stuff.
slouken@2713
    93
 */
slouken@2713
    94
extern HWND SDL_HelperWindow;
slouken@2713
    95
slouken@2713
    96
slouken@2713
    97
/*
slouken@2713
    98
 * Prototypes.
slouken@2713
    99
 */
icculus@7037
   100
static int DI_SetError(const char *str, HRESULT err);
slouken@2713
   101
static int DI_GUIDIsSame(const GUID * a, const GUID * b);
slouken@2713
   102
static int SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic,
slouken@2713
   103
                                          DIDEVICEINSTANCE instance);
icculus@6716
   104
static int SDL_SYS_HapticOpenFromDevice8(SDL_Haptic * haptic,
slouken@7319
   105
                                         LPDIRECTINPUTDEVICE8 device8,
slouken@7319
   106
                                         SDL_bool is_joystick);
icculus@8179
   107
static int SDL_SYS_HapticOpenFromXInput(SDL_Haptic * haptic, const Uint8 userid);
slouken@2713
   108
static DWORD DIGetTriggerButton(Uint16 button);
slouken@2713
   109
static int SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir,
slouken@2713
   110
                                int naxes);
slouken@2713
   111
static int SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest,
slouken@2713
   112
                              SDL_HapticEffect * src);
slouken@2713
   113
static void SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type);
slouken@2713
   114
static REFGUID SDL_SYS_HapticEffectType(SDL_HapticEffect * effect);
icculus@7481
   115
static int SDLCALL SDL_RunXInputHaptic(void *arg);
icculus@7481
   116
slouken@2713
   117
/* Callbacks. */
slouken@2713
   118
static BOOL CALLBACK EnumHapticsCallback(const DIDEVICEINSTANCE *
slouken@2713
   119
                                         pdidInstance, VOID * pContext);
slouken@2713
   120
static BOOL CALLBACK DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv);
slouken@2713
   121
slouken@2713
   122
slouken@7191
   123
/*
slouken@2713
   124
 * Like SDL_SetError but for DX error codes.
slouken@2713
   125
 */
icculus@7037
   126
static int
slouken@2713
   127
DI_SetError(const char *str, HRESULT err)
slouken@2713
   128
{
slouken@2786
   129
    /*
slouken@2786
   130
       SDL_SetError("Haptic: %s - %s: %s", str,
slouken@2786
   131
       DXGetErrorString8A(err), DXGetErrorDescription8A(err));
slouken@2786
   132
     */
icculus@7037
   133
    return SDL_SetError("Haptic error %s", str);
slouken@2713
   134
}
slouken@2713
   135
slouken@2713
   136
slouken@2713
   137
/*
slouken@2713
   138
 * Checks to see if two GUID are the same.
slouken@2713
   139
 */
slouken@2713
   140
static int
slouken@2713
   141
DI_GUIDIsSame(const GUID * a, const GUID * b)
slouken@2713
   142
{
icculus@6403
   143
    return (SDL_memcmp(a, b, sizeof (GUID)) == 0);
slouken@2713
   144
}
slouken@2713
   145
slouken@2713
   146
slouken@2713
   147
/*
slouken@2713
   148
 * Initializes the haptic subsystem.
slouken@2713
   149
 */
slouken@2713
   150
int
slouken@2713
   151
SDL_SYS_HapticInit(void)
slouken@2713
   152
{
icculus@6990
   153
    const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED);
slouken@2713
   154
    HRESULT ret;
slouken@2713
   155
    HINSTANCE instance;
slouken@2713
   156
slouken@2713
   157
    if (dinput != NULL) {       /* Already open. */
icculus@7037
   158
        return SDL_SetError("Haptic: SubSystem already open.");
slouken@2713
   159
    }
slouken@2713
   160
icculus@5591
   161
    ret = WIN_CoInitialize();
slouken@2713
   162
    if (FAILED(ret)) {
icculus@7037
   163
        return DI_SetError("Coinitialize", ret);
slouken@2713
   164
    }
slouken@2713
   165
icculus@5591
   166
    coinitialized = SDL_TRUE;
icculus@5591
   167
icculus@6716
   168
    ret = CoCreateInstance(&CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER,
icculus@6716
   169
                           &IID_IDirectInput8, (LPVOID) & dinput);
slouken@2713
   170
    if (FAILED(ret)) {
icculus@5591
   171
        SDL_SYS_HapticQuit();
icculus@7037
   172
        return DI_SetError("CoCreateInstance", ret);
slouken@2713
   173
    }
slouken@2713
   174
slouken@2713
   175
    /* Because we used CoCreateInstance, we need to Initialize it, first. */
slouken@2713
   176
    instance = GetModuleHandle(NULL);
slouken@2713
   177
    if (instance == NULL) {
icculus@5591
   178
        SDL_SYS_HapticQuit();
icculus@7037
   179
        return SDL_SetError("GetModuleHandle() failed with error code %d.",
icculus@7037
   180
                            GetLastError());
slouken@2713
   181
    }
icculus@6716
   182
    ret = IDirectInput8_Initialize(dinput, instance, DIRECTINPUT_VERSION);
slouken@2713
   183
    if (FAILED(ret)) {
icculus@5591
   184
        SDL_SYS_HapticQuit();
icculus@7037
   185
        return DI_SetError("Initializing DirectInput device", ret);
slouken@2713
   186
    }
slouken@2713
   187
slouken@2713
   188
    /* Look for haptic devices. */
icculus@6716
   189
    ret = IDirectInput8_EnumDevices(dinput,
slouken@2714
   190
                                   0,
slouken@2713
   191
                                   EnumHapticsCallback,
slouken@2713
   192
                                   NULL,
slouken@2713
   193
                                   DIEDFL_FORCEFEEDBACK |
slouken@2713
   194
                                   DIEDFL_ATTACHEDONLY);
slouken@2713
   195
    if (FAILED(ret)) {
icculus@5591
   196
        SDL_SYS_HapticQuit();
icculus@7037
   197
        return DI_SetError("Enumerating DirectInput devices", ret);
slouken@2713
   198
    }
slouken@2713
   199
icculus@6990
   200
    if (!env || SDL_atoi(env)) {
icculus@6990
   201
        loaded_xinput = (WIN_LoadXInputDLL() == 0);
icculus@6990
   202
    }
icculus@6990
   203
icculus@6990
   204
    if (loaded_xinput) {
icculus@6990
   205
        DWORD i;
icculus@8179
   206
        for (i = 0; i < SDL_XINPUT_MAX_DEVICES; i++) {
icculus@8179
   207
            XInputHaptic_MaybeAddDevice(i);
icculus@6990
   208
        }
icculus@6990
   209
    }
icculus@6990
   210
icculus@8179
   211
    return numhaptics;
slouken@2713
   212
}
slouken@2713
   213
icculus@8179
   214
icculus@8179
   215
int
icculus@8179
   216
DirectInputHaptic_MaybeAddDevice(const DIDEVICEINSTANCE * pdidInstance)
icculus@8179
   217
{
icculus@8179
   218
    HRESULT ret;
icculus@8179
   219
    LPDIRECTINPUTDEVICE8 device;
icculus@8179
   220
    const DWORD needflags = DIDC_ATTACHED | DIDC_FORCEFEEDBACK;
icculus@8179
   221
    DIDEVCAPS capabilities;
icculus@8179
   222
    SDL_hapticlist_item *item = NULL;
icculus@8179
   223
icculus@8185
   224
    if (dinput == NULL) {
icculus@8185
   225
        return -1;  /* not initialized. We'll pick these up on enumeration if we init later. */
icculus@8185
   226
    }
icculus@8185
   227
icculus@8179
   228
    /* Make sure we don't already have it */
icculus@8179
   229
    for (item = SDL_hapticlist; item; item = item->next) {
icculus@8179
   230
        if ( (!item->bXInputHaptic) && (SDL_memcmp(&item->instance, pdidInstance, sizeof (*pdidInstance)) == 0) ) {
icculus@8179
   231
            return -1;  /* Already added */
icculus@8179
   232
        }
icculus@8179
   233
    }
icculus@8179
   234
icculus@8179
   235
    /* Open the device */
icculus@8179
   236
    ret = IDirectInput8_CreateDevice(dinput, &pdidInstance->guidInstance, &device, NULL);
icculus@8179
   237
    if (FAILED(ret)) {
icculus@8179
   238
        /* DI_SetError("Creating DirectInput device",ret); */
icculus@8179
   239
        return -1;
icculus@8179
   240
    }
icculus@8179
   241
icculus@8179
   242
    /* Get capabilities. */
icculus@8179
   243
    SDL_zero(capabilities);
icculus@8179
   244
    capabilities.dwSize = sizeof (DIDEVCAPS);
icculus@8179
   245
    ret = IDirectInputDevice8_GetCapabilities(device, &capabilities);
icculus@8179
   246
    IDirectInputDevice8_Release(device);
icculus@8179
   247
    if (FAILED(ret)) {
icculus@8179
   248
        /* DI_SetError("Getting device capabilities",ret); */
icculus@8179
   249
        return -1;
icculus@8179
   250
    }
icculus@8179
   251
icculus@8179
   252
    if ((capabilities.dwFlags & needflags) != needflags) {
icculus@8179
   253
        return -1;  /* not a device we can use. */
icculus@8179
   254
    }
icculus@8179
   255
gabomdq@8247
   256
    item = (SDL_hapticlist_item *)SDL_calloc(1, sizeof(SDL_hapticlist_item));
icculus@8179
   257
    if (item == NULL) {
icculus@8179
   258
        return SDL_OutOfMemory();
icculus@8179
   259
    }
icculus@8179
   260
icculus@8179
   261
    item->name = WIN_StringToUTF8(pdidInstance->tszProductName);
icculus@8179
   262
    if (!item->name) {
icculus@8179
   263
        SDL_free(item);
icculus@8179
   264
        return -1;
icculus@8179
   265
    }
icculus@8179
   266
icculus@8179
   267
    /* Copy the instance over, useful for creating devices. */
icculus@8179
   268
    SDL_memcpy(&item->instance, pdidInstance, sizeof (DIDEVICEINSTANCE));
icculus@8179
   269
    SDL_memcpy(&item->capabilities, &capabilities, sizeof (capabilities));
icculus@8179
   270
icculus@8179
   271
    if (SDL_hapticlist_tail == NULL) {
icculus@8179
   272
        SDL_hapticlist = SDL_hapticlist_tail = item;
icculus@8179
   273
    } else {
icculus@8179
   274
        SDL_hapticlist_tail->next = item;
icculus@8179
   275
        SDL_hapticlist_tail = item;
icculus@8179
   276
    }
icculus@8179
   277
icculus@8179
   278
    /* Device has been added. */
icculus@8179
   279
    ++numhaptics;
icculus@8179
   280
icculus@8179
   281
    return numhaptics;
icculus@8179
   282
}
icculus@8179
   283
icculus@8179
   284
icculus@8179
   285
int
icculus@8179
   286
DirectInputHaptic_MaybeRemoveDevice(const DIDEVICEINSTANCE * pdidInstance)
icculus@8179
   287
{
icculus@8179
   288
    SDL_hapticlist_item *item;
icculus@8179
   289
    SDL_hapticlist_item *prev = NULL;
icculus@8179
   290
icculus@8185
   291
    if (dinput == NULL) {
icculus@8185
   292
        return -1;  /* not initialized, ignore this. */
icculus@8185
   293
    }
icculus@8185
   294
icculus@8179
   295
    for (item = SDL_hapticlist; item != NULL; item = item->next) {
icculus@8179
   296
        if ( (!item->bXInputHaptic) && (SDL_memcmp(&item->instance, pdidInstance, sizeof (*pdidInstance)) == 0) ) {
icculus@8179
   297
            /* found it, remove it. */
icculus@8179
   298
            const int retval = item->haptic ? item->haptic->index : -1;
icculus@8179
   299
            if (prev != NULL) {
icculus@8179
   300
                prev->next = item->next;
icculus@8179
   301
            } else {
icculus@8179
   302
                SDL_assert(SDL_hapticlist == item);
icculus@8179
   303
                SDL_hapticlist = item->next;
icculus@8179
   304
            }
icculus@8179
   305
            if (item == SDL_hapticlist_tail) {
icculus@8179
   306
                SDL_hapticlist_tail = prev;
icculus@8179
   307
            }
icculus@8179
   308
            --numhaptics;
icculus@8179
   309
            /* !!! TODO: Send a haptic remove event? */
icculus@8179
   310
            SDL_free(item);
icculus@8179
   311
            return retval;
icculus@8179
   312
        }
icculus@8179
   313
        prev = item;
icculus@8179
   314
    }
icculus@8179
   315
icculus@8179
   316
    return -1;
icculus@8179
   317
}
icculus@8179
   318
icculus@8179
   319
icculus@8179
   320
int
icculus@8179
   321
XInputHaptic_MaybeAddDevice(const DWORD dwUserid)
icculus@8179
   322
{
icculus@8179
   323
    const Uint8 userid = (Uint8) dwUserid;
icculus@8179
   324
    XINPUT_CAPABILITIES caps;
icculus@8179
   325
    const SDL_bool bIs14OrLater = (SDL_XInputVersion >= ((1<<16)|4));
icculus@8179
   326
    SDL_hapticlist_item *item;
icculus@8179
   327
icculus@8179
   328
    if ((!loaded_xinput) || (dwUserid >= SDL_XINPUT_MAX_DEVICES)) {
icculus@8179
   329
        return -1;
icculus@8179
   330
    }
icculus@8179
   331
icculus@8179
   332
    /* Make sure we don't already have it */
icculus@8179
   333
    for (item = SDL_hapticlist; item; item = item->next) {
icculus@8179
   334
        if ((item->bXInputHaptic) && (item->userid == userid)) {
icculus@8179
   335
            return -1;  /* Already added */
icculus@8179
   336
        }
icculus@8179
   337
    }
icculus@8179
   338
icculus@8179
   339
    if (XINPUTGETCAPABILITIES(dwUserid, XINPUT_FLAG_GAMEPAD, &caps) != ERROR_SUCCESS) {
icculus@8179
   340
        return -1;  /* maybe controller isn't plugged in. */
icculus@8179
   341
    }
icculus@8179
   342
icculus@8179
   343
    /* XInput < 1.4 is probably only for original XBox360 controllers,
icculus@8179
   344
        which don't offer the flag, and always have force feedback */
icculus@8179
   345
    if ( (bIs14OrLater) && ((caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) == 0) ) {
icculus@8179
   346
        return -1;  /* no force feedback on this device. */
icculus@8179
   347
    }
icculus@8179
   348
icculus@8179
   349
    item = (SDL_hapticlist_item *)SDL_malloc( sizeof(SDL_hapticlist_item));
icculus@8179
   350
    if (item == NULL) {
icculus@8179
   351
        return SDL_OutOfMemory();
icculus@8179
   352
    }
icculus@8179
   353
icculus@8179
   354
    SDL_zerop(item);
icculus@8179
   355
icculus@8179
   356
    /* !!! FIXME: I'm not bothering to query for a real name right now (can we even?) */
icculus@8179
   357
    {
icculus@8179
   358
        char buf[64];
icculus@8179
   359
        SDL_snprintf(buf, sizeof (buf), "XInput Controller #%u", (unsigned int) (userid+1));
icculus@8179
   360
        item->name = SDL_strdup(buf);
icculus@8179
   361
    }
icculus@8179
   362
icculus@8179
   363
    if (!item->name) {
icculus@8179
   364
        SDL_free(item);
icculus@8179
   365
        return -1;
icculus@8179
   366
    }
icculus@8179
   367
icculus@8179
   368
    /* Copy the instance over, useful for creating devices. */
icculus@8179
   369
    item->bXInputHaptic = 1;
icculus@8179
   370
    item->userid = userid;
icculus@8179
   371
icculus@8179
   372
    if (SDL_hapticlist_tail == NULL) {
icculus@8179
   373
        SDL_hapticlist = SDL_hapticlist_tail = item;
icculus@8179
   374
    } else {
icculus@8179
   375
        SDL_hapticlist_tail->next = item;
icculus@8179
   376
        SDL_hapticlist_tail = item;
icculus@8179
   377
    }
icculus@8179
   378
icculus@8179
   379
    /* Device has been added. */
icculus@8179
   380
    ++numhaptics;
icculus@8179
   381
icculus@8179
   382
    return numhaptics;
icculus@8179
   383
}
icculus@8179
   384
icculus@8179
   385
icculus@8179
   386
int
icculus@8179
   387
XInputHaptic_MaybeRemoveDevice(const DWORD dwUserid)
icculus@8179
   388
{
icculus@8179
   389
    const Uint8 userid = (Uint8) dwUserid;
icculus@8179
   390
    SDL_hapticlist_item *item;
icculus@8179
   391
    SDL_hapticlist_item *prev = NULL;
icculus@8179
   392
icculus@8179
   393
    if ((!loaded_xinput) || (dwUserid >= SDL_XINPUT_MAX_DEVICES)) {
icculus@8179
   394
        return -1;
icculus@8179
   395
    }
icculus@8179
   396
icculus@8179
   397
    for (item = SDL_hapticlist; item != NULL; item = item->next) {
icculus@8179
   398
        if ((item->bXInputHaptic) && (item->userid == userid)) {
icculus@8179
   399
            /* found it, remove it. */
icculus@8179
   400
            const int retval = item->haptic ? item->haptic->index : -1;
icculus@8179
   401
            if (prev != NULL) {
icculus@8179
   402
                prev->next = item->next;
icculus@8179
   403
            } else {
icculus@8179
   404
                SDL_assert(SDL_hapticlist == item);
icculus@8179
   405
                SDL_hapticlist = item->next;
icculus@8179
   406
            }
icculus@8179
   407
            if (item == SDL_hapticlist_tail) {
icculus@8179
   408
                SDL_hapticlist_tail = prev;
icculus@8179
   409
            }
icculus@8179
   410
            --numhaptics;
icculus@8179
   411
            /* !!! TODO: Send a haptic remove event? */
icculus@8179
   412
            SDL_free(item);
icculus@8179
   413
            return retval;
icculus@8179
   414
        }
icculus@8179
   415
        prev = item;
icculus@8179
   416
    }
icculus@8179
   417
icculus@8179
   418
    return -1;
icculus@8179
   419
}
icculus@8179
   420
icculus@8179
   421
slouken@2713
   422
/*
slouken@2713
   423
 * Callback to find the haptic devices.
slouken@2713
   424
 */
slouken@2713
   425
static BOOL CALLBACK
slouken@2713
   426
EnumHapticsCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
slouken@2713
   427
{
icculus@8179
   428
    (void) pContext;
icculus@8179
   429
    DirectInputHaptic_MaybeAddDevice(pdidInstance);
icculus@8179
   430
    return DIENUM_CONTINUE;  /* continue enumerating */
icculus@8179
   431
}
slouken@2713
   432
slouken@2713
   433
icculus@8179
   434
int
icculus@8179
   435
SDL_SYS_NumHaptics()
icculus@8179
   436
{
icculus@8179
   437
    return numhaptics;
icculus@8179
   438
}
icculus@8179
   439
icculus@8179
   440
static SDL_hapticlist_item *
icculus@8179
   441
HapticByDevIndex(int device_index)
icculus@8179
   442
{
icculus@8179
   443
    SDL_hapticlist_item *item = SDL_hapticlist;
icculus@8179
   444
icculus@8179
   445
    if ((device_index < 0) || (device_index >= numhaptics)) {
icculus@8179
   446
        return NULL;
slouken@2713
   447
    }
slouken@2713
   448
icculus@8179
   449
    while (device_index > 0) {
icculus@8179
   450
        SDL_assert(item != NULL);
urkle@8183
   451
        --device_index;
icculus@8179
   452
        item = item->next;
slouken@2713
   453
    }
slouken@2713
   454
icculus@8179
   455
    return item;
slouken@2713
   456
}
slouken@2713
   457
slouken@2713
   458
/*
slouken@2713
   459
 * Return the name of a haptic device, does not need to be opened.
slouken@2713
   460
 */
slouken@2713
   461
const char *
slouken@2713
   462
SDL_SYS_HapticName(int index)
slouken@2713
   463
{
icculus@8179
   464
    SDL_hapticlist_item *item = HapticByDevIndex(index);
icculus@8179
   465
    return item->name;
slouken@2713
   466
}
slouken@2713
   467
slouken@2713
   468
slouken@2713
   469
/*
slouken@2713
   470
 * Callback to get all supported effects.
slouken@2713
   471
 */
slouken@2713
   472
#define EFFECT_TEST(e,s)               \
slouken@2713
   473
if (DI_GUIDIsSame(&pei->guid, &(e)))   \
slouken@2713
   474
   haptic->supported |= (s)
slouken@2713
   475
static BOOL CALLBACK
slouken@2713
   476
DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv)
slouken@2713
   477
{
slouken@2713
   478
    /* Prepare the haptic device. */
slouken@2713
   479
    SDL_Haptic *haptic = (SDL_Haptic *) pv;
slouken@2713
   480
slouken@2713
   481
    /* Get supported. */
slouken@2713
   482
    EFFECT_TEST(GUID_Spring, SDL_HAPTIC_SPRING);
slouken@2713
   483
    EFFECT_TEST(GUID_Damper, SDL_HAPTIC_DAMPER);
slouken@2713
   484
    EFFECT_TEST(GUID_Inertia, SDL_HAPTIC_INERTIA);
slouken@2713
   485
    EFFECT_TEST(GUID_Friction, SDL_HAPTIC_FRICTION);
slouken@2713
   486
    EFFECT_TEST(GUID_ConstantForce, SDL_HAPTIC_CONSTANT);
slouken@2713
   487
    EFFECT_TEST(GUID_CustomForce, SDL_HAPTIC_CUSTOM);
slouken@2713
   488
    EFFECT_TEST(GUID_Sine, SDL_HAPTIC_SINE);
icculus@7621
   489
    /* !!! FIXME: put this back when we have more bits in 2.1 */
gabomdq@7678
   490
    /* EFFECT_TEST(GUID_Square, SDL_HAPTIC_SQUARE); */
slouken@2713
   491
    EFFECT_TEST(GUID_Triangle, SDL_HAPTIC_TRIANGLE);
slouken@2713
   492
    EFFECT_TEST(GUID_SawtoothUp, SDL_HAPTIC_SAWTOOTHUP);
slouken@2713
   493
    EFFECT_TEST(GUID_SawtoothDown, SDL_HAPTIC_SAWTOOTHDOWN);
slouken@2713
   494
    EFFECT_TEST(GUID_RampForce, SDL_HAPTIC_RAMP);
slouken@2713
   495
slouken@2713
   496
    /* Check for more. */
slouken@2713
   497
    return DIENUM_CONTINUE;
slouken@2713
   498
}
slouken@2713
   499
slouken@2713
   500
slouken@2713
   501
/*
slouken@2713
   502
 * Callback to get supported axes.
slouken@2713
   503
 */
slouken@2713
   504
static BOOL CALLBACK
slouken@2713
   505
DI_DeviceObjectCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
slouken@2713
   506
{
slouken@2713
   507
    SDL_Haptic *haptic = (SDL_Haptic *) pvRef;
slouken@2713
   508
slouken@2713
   509
    if ((dev->dwType & DIDFT_AXIS) && (dev->dwFlags & DIDOI_FFACTUATOR)) {
icculus@8634
   510
        const GUID *guid = &dev->guidType;
icculus@8634
   511
        DWORD offset = 0;
icculus@8634
   512
        if (DI_GUIDIsSame(guid, &GUID_XAxis)) {
icculus@8634
   513
            offset = DIJOFS_X;
icculus@8634
   514
        } else if (DI_GUIDIsSame(guid, &GUID_YAxis)) {
icculus@8634
   515
            offset = DIJOFS_Y;
icculus@8634
   516
        } else if (DI_GUIDIsSame(guid, &GUID_ZAxis)) {
icculus@8634
   517
            offset = DIJOFS_Z;
icculus@8634
   518
        } else if (DI_GUIDIsSame(guid, &GUID_RxAxis)) {
icculus@8634
   519
            offset = DIJOFS_RX;
icculus@8634
   520
        } else if (DI_GUIDIsSame(guid, &GUID_RyAxis)) {
icculus@8634
   521
            offset = DIJOFS_RY;
icculus@8634
   522
        } else if (DI_GUIDIsSame(guid, &GUID_RzAxis)) {
icculus@8634
   523
            offset = DIJOFS_RZ;
icculus@8634
   524
        } else {
icculus@8634
   525
            return DIENUM_CONTINUE;   /* can't use this, go on. */
icculus@8634
   526
        }
slouken@2713
   527
icculus@8634
   528
        haptic->hwdata->axes[haptic->naxes] = offset;
slouken@2713
   529
        haptic->naxes++;
slouken@2713
   530
slouken@2713
   531
        /* Currently using the artificial limit of 3 axes. */
slouken@2713
   532
        if (haptic->naxes >= 3) {
slouken@2713
   533
            return DIENUM_STOP;
slouken@2713
   534
        }
slouken@2713
   535
    }
slouken@2713
   536
slouken@2713
   537
    return DIENUM_CONTINUE;
slouken@2713
   538
}
slouken@2713
   539
slouken@2713
   540
slouken@2713
   541
/*
slouken@2713
   542
 * Opens the haptic device from the file descriptor.
slouken@2713
   543
 *
slouken@2713
   544
 *    Steps:
slouken@2713
   545
 *       - Open temporary DirectInputDevice interface.
icculus@6716
   546
 *       - Create DirectInputDevice8 interface.
slouken@2713
   547
 *       - Release DirectInputDevice interface.
icculus@6716
   548
 *       - Call SDL_SYS_HapticOpenFromDevice8
slouken@2713
   549
 */
slouken@2713
   550
static int
slouken@2713
   551
SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic, DIDEVICEINSTANCE instance)
slouken@2713
   552
{
slouken@2713
   553
    HRESULT ret;
slouken@2713
   554
    int ret2;
icculus@6716
   555
    LPDIRECTINPUTDEVICE8 device;
slouken@7319
   556
    LPDIRECTINPUTDEVICE8 device8;
slouken@2713
   557
slouken@2713
   558
    /* Open the device */
icculus@6716
   559
    ret = IDirectInput8_CreateDevice(dinput, &instance.guidInstance,
slouken@2713
   560
                                    &device, NULL);
slouken@2713
   561
    if (FAILED(ret)) {
slouken@2713
   562
        DI_SetError("Creating DirectInput device", ret);
slouken@7319
   563
        return -1;
slouken@2713
   564
    }
slouken@2713
   565
icculus@6716
   566
    /* Now get the IDirectInputDevice8 interface, instead. */
icculus@6716
   567
    ret = IDirectInputDevice8_QueryInterface(device,
icculus@6716
   568
                                            &IID_IDirectInputDevice8,
slouken@7319
   569
                                            (LPVOID *) &device8);
slouken@2713
   570
    /* Done with the temporary one now. */
icculus@6716
   571
    IDirectInputDevice8_Release(device);
slouken@2713
   572
    if (FAILED(ret)) {
slouken@2713
   573
        DI_SetError("Querying DirectInput interface", ret);
slouken@7319
   574
        return -1;
slouken@2713
   575
    }
slouken@2713
   576
slouken@7319
   577
    ret2 = SDL_SYS_HapticOpenFromDevice8(haptic, device8, SDL_FALSE);
slouken@2713
   578
    if (ret2 < 0) {
slouken@7319
   579
        IDirectInputDevice8_Release(device8);
slouken@7319
   580
        return -1;
slouken@2713
   581
    }
slouken@2713
   582
slouken@2713
   583
    return 0;
slouken@2713
   584
}
slouken@2713
   585
icculus@6990
   586
static int
icculus@8179
   587
SDL_SYS_HapticOpenFromXInput(SDL_Haptic *haptic, const Uint8 userid)
icculus@6990
   588
{
icculus@7481
   589
    char threadName[32];
icculus@6990
   590
    XINPUT_VIBRATION vibration = { 0, 0 };  /* stop any current vibration */
icculus@6990
   591
    XINPUTSETSTATE(userid, &vibration);
icculus@6990
   592
icculus@7621
   593
    haptic->supported = SDL_HAPTIC_LEFTRIGHT;
icculus@6990
   594
icculus@6990
   595
    haptic->neffects = 1;
icculus@6990
   596
    haptic->nplaying = 1;
icculus@6990
   597
icculus@6990
   598
    /* Prepare effects memory. */
icculus@6990
   599
    haptic->effects = (struct haptic_effect *)
icculus@6990
   600
        SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
icculus@6990
   601
    if (haptic->effects == NULL) {
icculus@7037
   602
        return SDL_OutOfMemory();
icculus@6990
   603
    }
icculus@6990
   604
    /* Clear the memory */
icculus@6990
   605
    SDL_memset(haptic->effects, 0,
icculus@6990
   606
               sizeof(struct haptic_effect) * haptic->neffects);
icculus@6990
   607
icculus@6990
   608
    haptic->hwdata = (struct haptic_hwdata *) SDL_malloc(sizeof(*haptic->hwdata));
icculus@6990
   609
    if (haptic->hwdata == NULL) {
icculus@6990
   610
        SDL_free(haptic->effects);
icculus@6990
   611
        haptic->effects = NULL;
icculus@7037
   612
        return SDL_OutOfMemory();
icculus@6990
   613
    }
icculus@6990
   614
    SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
icculus@6990
   615
icculus@6990
   616
    haptic->hwdata->bXInputHaptic = 1;
icculus@6990
   617
    haptic->hwdata->userid = userid;
icculus@6990
   618
icculus@7481
   619
    haptic->hwdata->mutex = SDL_CreateMutex();
icculus@7481
   620
    if (haptic->hwdata->mutex == NULL) {
icculus@7481
   621
        SDL_free(haptic->effects);
icculus@7481
   622
        SDL_free(haptic->hwdata);
icculus@7481
   623
        haptic->effects = NULL;
icculus@7481
   624
        return SDL_SetError("Couldn't create XInput haptic mutex");
icculus@7481
   625
    }
icculus@7481
   626
icculus@7481
   627
    SDL_snprintf(threadName, sizeof (threadName), "SDLXInputDev%d", (int) userid);
icculus@7481
   628
icculus@7481
   629
#if defined(__WIN32__) && !defined(HAVE_LIBC)  /* !!! FIXME: this is nasty. */
icculus@7481
   630
    #undef SDL_CreateThread
icculus@8094
   631
    #if SDL_DYNAMIC_API
icculus@8094
   632
    haptic->hwdata->thread = SDL_CreateThread_REAL(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL);
icculus@8094
   633
    #else
icculus@7481
   634
    haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL);
icculus@8094
   635
    #endif
icculus@7481
   636
#else
icculus@7481
   637
    haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata);
icculus@7481
   638
#endif
icculus@7481
   639
    if (haptic->hwdata->thread == NULL) {
icculus@7481
   640
        SDL_DestroyMutex(haptic->hwdata->mutex);
icculus@7481
   641
        SDL_free(haptic->effects);
icculus@7481
   642
        SDL_free(haptic->hwdata);
icculus@7481
   643
        haptic->effects = NULL;
icculus@7481
   644
        return SDL_SetError("Couldn't create XInput haptic thread");
icculus@7481
   645
    }
icculus@7481
   646
icculus@6990
   647
    return 0;
icculus@6990
   648
 }
slouken@2713
   649
slouken@2713
   650
/*
slouken@2713
   651
 * Opens the haptic device from the file descriptor.
slouken@2713
   652
 *
slouken@2713
   653
 *    Steps:
slouken@2713
   654
 *       - Set cooperative level.
slouken@2713
   655
 *       - Set data format.
slouken@2713
   656
 *       - Acquire exclusiveness.
slouken@2713
   657
 *       - Reset actuators.
philipp@7132
   658
 *       - Get supported features.
slouken@2713
   659
 */
slouken@2713
   660
static int
icculus@6716
   661
SDL_SYS_HapticOpenFromDevice8(SDL_Haptic * haptic,
slouken@7319
   662
                              LPDIRECTINPUTDEVICE8 device8, SDL_bool is_joystick)
slouken@2713
   663
{
slouken@2713
   664
    HRESULT ret;
slouken@2713
   665
    DIPROPDWORD dipdw;
slouken@2713
   666
slouken@7319
   667
    /* Allocate the hwdata */
slouken@7319
   668
    haptic->hwdata = (struct haptic_hwdata *)SDL_malloc(sizeof(*haptic->hwdata));
slouken@7319
   669
    if (haptic->hwdata == NULL) {
slouken@7319
   670
        return SDL_OutOfMemory();
slouken@7319
   671
    }
slouken@7319
   672
    SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
slouken@7319
   673
icculus@6716
   674
    /* We'll use the device8 from now on. */
icculus@6716
   675
    haptic->hwdata->device = device8;
slouken@7319
   676
    haptic->hwdata->is_joystick = is_joystick;
slouken@2713
   677
icculus@8629
   678
    /* !!! FIXME: opening a haptic device here first will make an attempt to
icculus@8629
   679
       !!! FIXME:  SDL_JoystickOpen() that same device fail later, since we
icculus@8629
   680
       !!! FIXME:  have it open in exclusive mode. But this will allow
icculus@8629
   681
       !!! FIXME:  SDL_JoystickOpen() followed by SDL_HapticOpenFromJoystick()
icculus@8629
   682
       !!! FIXME:  to work, and that's probably the common case. Still,
icculus@8629
   683
       !!! FIXME:  ideally, We need to unify the opening code. */
slouken@2713
   684
icculus@8629
   685
    if (!is_joystick) {  /* if is_joystick, we already set this up elsewhere. */
icculus@8629
   686
        /* Grab it exclusively to use force feedback stuff. */
icculus@8629
   687
        ret = IDirectInputDevice8_SetCooperativeLevel(haptic->hwdata->device,
icculus@8629
   688
                                                      SDL_HelperWindow,
icculus@8629
   689
                                                      DISCL_EXCLUSIVE |
icculus@8629
   690
                                                      DISCL_BACKGROUND);
icculus@8629
   691
        if (FAILED(ret)) {
icculus@8629
   692
            DI_SetError("Setting cooperative level to exclusive", ret);
icculus@8629
   693
            goto acquire_err;
icculus@8629
   694
        }
slouken@2713
   695
icculus@8629
   696
        /* Set data format. */
icculus@8629
   697
        ret = IDirectInputDevice8_SetDataFormat(haptic->hwdata->device,
icculus@8629
   698
                                                &c_dfDIJoystick2);
icculus@8629
   699
        if (FAILED(ret)) {
icculus@8629
   700
            DI_SetError("Setting data format", ret);
icculus@8629
   701
            goto acquire_err;
icculus@8629
   702
        }
slouken@2713
   703
icculus@8629
   704
        /* Get number of axes. */
icculus@8629
   705
        ret = IDirectInputDevice8_EnumObjects(haptic->hwdata->device,
icculus@8629
   706
                                              DI_DeviceObjectCallback,
icculus@8629
   707
                                              haptic, DIDFT_AXIS);
icculus@8629
   708
        if (FAILED(ret)) {
icculus@8629
   709
            DI_SetError("Getting device axes", ret);
icculus@8629
   710
            goto acquire_err;
icculus@8629
   711
        }
icculus@8629
   712
icculus@8629
   713
        /* Acquire the device. */
icculus@8629
   714
        ret = IDirectInputDevice8_Acquire(haptic->hwdata->device);
icculus@8629
   715
        if (FAILED(ret)) {
icculus@8629
   716
            DI_SetError("Acquiring DirectInput device", ret);
icculus@8629
   717
            goto acquire_err;
icculus@8629
   718
        }
slouken@2713
   719
    }
slouken@2713
   720
slouken@2713
   721
    /* Reset all actuators - just in case. */
icculus@6716
   722
    ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
slouken@2713
   723
                                                       DISFFC_RESET);
slouken@2713
   724
    if (FAILED(ret)) {
slouken@2713
   725
        DI_SetError("Resetting device", ret);
slouken@2713
   726
        goto acquire_err;
slouken@2713
   727
    }
slouken@2713
   728
slouken@2713
   729
    /* Enabling actuators. */
icculus@6716
   730
    ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
slouken@2713
   731
                                                       DISFFC_SETACTUATORSON);
slouken@2713
   732
    if (FAILED(ret)) {
slouken@2713
   733
        DI_SetError("Enabling actuators", ret);
slouken@2713
   734
        goto acquire_err;
slouken@2713
   735
    }
slouken@2713
   736
slouken@2713
   737
    /* Get supported effects. */
icculus@6716
   738
    ret = IDirectInputDevice8_EnumEffects(haptic->hwdata->device,
slouken@2713
   739
                                          DI_EffectCallback, haptic,
slouken@2713
   740
                                          DIEFT_ALL);
slouken@2713
   741
    if (FAILED(ret)) {
slouken@2713
   742
        DI_SetError("Enumerating supported effects", ret);
slouken@2713
   743
        goto acquire_err;
slouken@2713
   744
    }
slouken@2713
   745
    if (haptic->supported == 0) {       /* Error since device supports nothing. */
slouken@2713
   746
        SDL_SetError("Haptic: Internal error on finding supported effects.");
slouken@2713
   747
        goto acquire_err;
slouken@2713
   748
    }
slouken@2713
   749
slouken@2713
   750
    /* Check autogain and autocenter. */
slouken@2713
   751
    dipdw.diph.dwSize = sizeof(DIPROPDWORD);
slouken@2713
   752
    dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
slouken@2713
   753
    dipdw.diph.dwObj = 0;
slouken@2713
   754
    dipdw.diph.dwHow = DIPH_DEVICE;
slouken@2713
   755
    dipdw.dwData = 10000;
icculus@6716
   756
    ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
slouken@2713
   757
                                          DIPROP_FFGAIN, &dipdw.diph);
slouken@2713
   758
    if (!FAILED(ret)) {         /* Gain is supported. */
slouken@2713
   759
        haptic->supported |= SDL_HAPTIC_GAIN;
slouken@2713
   760
    }
slouken@2713
   761
    dipdw.diph.dwObj = 0;
slouken@2713
   762
    dipdw.diph.dwHow = DIPH_DEVICE;
slouken@2713
   763
    dipdw.dwData = DIPROPAUTOCENTER_OFF;
icculus@6716
   764
    ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
slouken@2713
   765
                                          DIPROP_AUTOCENTER, &dipdw.diph);
slouken@2713
   766
    if (!FAILED(ret)) {         /* Autocenter is supported. */
slouken@2713
   767
        haptic->supported |= SDL_HAPTIC_AUTOCENTER;
slouken@2713
   768
    }
slouken@2713
   769
slouken@2713
   770
    /* Status is always supported. */
slouken@2713
   771
    haptic->supported |= SDL_HAPTIC_STATUS | SDL_HAPTIC_PAUSE;
slouken@2713
   772
slouken@2713
   773
    /* Check maximum effects. */
slouken@2713
   774
    haptic->neffects = 128;     /* This is not actually supported as thus under windows,
slouken@2713
   775
                                   there is no way to tell the number of EFFECTS that a
slouken@2713
   776
                                   device can hold, so we'll just use a "random" number
slouken@2713
   777
                                   instead and put warnings in SDL_haptic.h */
slouken@2713
   778
    haptic->nplaying = 128;     /* Even more impossible to get this then neffects. */
slouken@2713
   779
slouken@2713
   780
    /* Prepare effects memory. */
slouken@2713
   781
    haptic->effects = (struct haptic_effect *)
slouken@2713
   782
        SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
slouken@2713
   783
    if (haptic->effects == NULL) {
slouken@2713
   784
        SDL_OutOfMemory();
slouken@2713
   785
        goto acquire_err;
slouken@2713
   786
    }
slouken@2713
   787
    /* Clear the memory */
slouken@2713
   788
    SDL_memset(haptic->effects, 0,
slouken@2713
   789
               sizeof(struct haptic_effect) * haptic->neffects);
slouken@2713
   790
slouken@2713
   791
    return 0;
slouken@2713
   792
slouken@2713
   793
    /* Error handling */
slouken@2713
   794
  acquire_err:
icculus@6716
   795
    IDirectInputDevice8_Unacquire(haptic->hwdata->device);
slouken@2713
   796
    return -1;
slouken@2713
   797
slouken@2713
   798
}
slouken@2713
   799
slouken@2713
   800
slouken@2713
   801
/*
slouken@2713
   802
 * Opens a haptic device for usage.
slouken@2713
   803
 */
slouken@2713
   804
int
slouken@2713
   805
SDL_SYS_HapticOpen(SDL_Haptic * haptic)
slouken@2713
   806
{
icculus@8179
   807
    SDL_hapticlist_item *item = HapticByDevIndex(haptic->index);
icculus@8179
   808
    return (item->bXInputHaptic) ? SDL_SYS_HapticOpenFromXInput(haptic, item->userid) : SDL_SYS_HapticOpenFromInstance(haptic, item->instance);
slouken@2713
   809
}
slouken@2713
   810
slouken@2713
   811
slouken@2713
   812
/*
slouken@2713
   813
 * Opens a haptic device from first mouse it finds for usage.
slouken@2713
   814
 */
slouken@2713
   815
int
slouken@2713
   816
SDL_SYS_HapticMouse(void)
slouken@2713
   817
{
icculus@8179
   818
    SDL_hapticlist_item *item;
urkle@8183
   819
    int index = 0;
slouken@2713
   820
slouken@2713
   821
    /* Grab the first mouse haptic device we find. */
icculus@8179
   822
    for (item = SDL_hapticlist; item != NULL; item = item->next) {
icculus@8179
   823
        SDL_assert(index >= 0);
icculus@8179
   824
        if (item->capabilities.dwDevType == DI8DEVCLASS_POINTER ) {
icculus@8179
   825
            return index;
slouken@2713
   826
        }
urkle@8183
   827
        ++index;
slouken@2713
   828
    }
slouken@2713
   829
slouken@2713
   830
    return -1;
slouken@2713
   831
}
slouken@2713
   832
slouken@2713
   833
slouken@2713
   834
/*
slouken@2713
   835
 * Checks to see if a joystick has haptic features.
slouken@2713
   836
 */
slouken@2713
   837
int
slouken@2713
   838
SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
slouken@2713
   839
{
icculus@6990
   840
    const struct joystick_hwdata *hwdata = joystick->hwdata;
icculus@6990
   841
    return ( (hwdata->bXInputHaptic) ||
icculus@6990
   842
             ((hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) != 0) );
slouken@2713
   843
}
slouken@2713
   844
slouken@2713
   845
slouken@2713
   846
/*
icculus@7708
   847
 * Checks to see if the haptic device and joystick are in reality the same.
slouken@2713
   848
 */
slouken@2713
   849
int
slouken@2713
   850
SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
slouken@2713
   851
{
icculus@7848
   852
    if (joystick->hwdata->bXInputHaptic != haptic->hwdata->bXInputHaptic) {
icculus@7846
   853
        return 0;  /* one is XInput, one is not; not the same device. */
icculus@7846
   854
    } else if (joystick->hwdata->bXInputHaptic) {  /* XInput */
icculus@7846
   855
        return (haptic->hwdata->userid == joystick->hwdata->userid);
icculus@7846
   856
    } else {  /* DirectInput */
icculus@6990
   857
        HRESULT ret;
icculus@6990
   858
        DIDEVICEINSTANCE hap_instance, joy_instance;
slouken@2713
   859
icculus@6990
   860
        hap_instance.dwSize = sizeof(DIDEVICEINSTANCE);
icculus@6990
   861
        joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
icculus@6990
   862
icculus@6990
   863
        /* Get the device instances. */
icculus@6990
   864
        ret = IDirectInputDevice8_GetDeviceInfo(haptic->hwdata->device,
slouken@2713
   865
                                            &hap_instance);
icculus@6990
   866
        if (FAILED(ret)) {
icculus@6990
   867
            return 0;
icculus@6990
   868
        }
icculus@6990
   869
        ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice,
icculus@6990
   870
                                                &joy_instance);
icculus@6990
   871
        if (FAILED(ret)) {
icculus@6990
   872
            return 0;
icculus@6990
   873
        }
icculus@6990
   874
icculus@6990
   875
        if (DI_GUIDIsSame(&hap_instance.guidInstance, &joy_instance.guidInstance))
icculus@6990
   876
            return 1;
slouken@2713
   877
    }
slouken@2713
   878
slouken@2713
   879
    return 0;
slouken@2713
   880
}
slouken@2713
   881
slouken@2713
   882
slouken@2713
   883
/*
slouken@2713
   884
 * Opens a SDL_Haptic from a SDL_Joystick.
slouken@2713
   885
 */
slouken@2713
   886
int
slouken@2713
   887
SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
slouken@2713
   888
{
icculus@8179
   889
    SDL_hapticlist_item *item;
urkle@8183
   890
    int index = 0;
slouken@5358
   891
slouken@5358
   892
    /* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */
icculus@6990
   893
    if (joystick->hwdata->bXInputDevice) {
icculus@6990
   894
        const Uint8 userid = joystick->hwdata->userid;
icculus@8179
   895
        for (item = SDL_hapticlist; item != NULL; item = item->next) {
icculus@8179
   896
            if ((item->bXInputHaptic) && (item->userid == userid)) {
icculus@6990
   897
                SDL_assert(joystick->hwdata->bXInputHaptic);
icculus@8179
   898
                haptic->index = index;
icculus@8179
   899
                return SDL_SYS_HapticOpenFromXInput(haptic, userid);
icculus@6990
   900
            }
urkle@8183
   901
            ++index;
slouken@5358
   902
        }
icculus@6990
   903
    } else {
icculus@8179
   904
        HRESULT idret;
icculus@8179
   905
        DIDEVICEINSTANCE joy_instance;
icculus@8179
   906
icculus@8179
   907
        joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
icculus@8179
   908
        idret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice, &joy_instance);
icculus@8179
   909
        if (FAILED(idret)) {
icculus@8179
   910
            return -1;
icculus@8179
   911
        }
icculus@8179
   912
icculus@8179
   913
        for (item = SDL_hapticlist; item != NULL; item = item->next) {
icculus@8179
   914
            if (DI_GUIDIsSame(&item->instance.guidInstance, &joy_instance.guidInstance)) {
icculus@8179
   915
                haptic->index = index;
slouken@7319
   916
                return SDL_SYS_HapticOpenFromDevice8(haptic, joystick->hwdata->InputDevice, SDL_TRUE);
icculus@6990
   917
            }
urkle@8183
   918
            ++index;
slouken@5358
   919
        }
slouken@5358
   920
    }
icculus@8179
   921
slouken@7319
   922
    /* No match to our haptic list */
slouken@7319
   923
    return -1;
slouken@2713
   924
}
slouken@2713
   925
slouken@2713
   926
slouken@2713
   927
/*
slouken@2713
   928
 * Closes the haptic device.
slouken@2713
   929
 */
slouken@2713
   930
void
slouken@2713
   931
SDL_SYS_HapticClose(SDL_Haptic * haptic)
slouken@2713
   932
{
slouken@2713
   933
    if (haptic->hwdata) {
slouken@2713
   934
slouken@2713
   935
        /* Free effects. */
slouken@2713
   936
        SDL_free(haptic->effects);
slouken@2713
   937
        haptic->effects = NULL;
slouken@2713
   938
        haptic->neffects = 0;
slouken@2713
   939
slouken@2713
   940
        /* Clean up */
icculus@7481
   941
        if (haptic->hwdata->bXInputHaptic) {
icculus@7481
   942
            haptic->hwdata->stopThread = 1;
icculus@7481
   943
            SDL_WaitThread(haptic->hwdata->thread, NULL);
icculus@7481
   944
            SDL_DestroyMutex(haptic->hwdata->mutex);
icculus@7481
   945
        } else {
icculus@6990
   946
            IDirectInputDevice8_Unacquire(haptic->hwdata->device);
icculus@6990
   947
            /* Only release if isn't grabbed by a joystick. */
icculus@6990
   948
            if (haptic->hwdata->is_joystick == 0) {
icculus@6990
   949
                IDirectInputDevice8_Release(haptic->hwdata->device);
icculus@6990
   950
            }
slouken@2713
   951
        }
slouken@2713
   952
slouken@2713
   953
        /* Free */
slouken@2713
   954
        SDL_free(haptic->hwdata);
slouken@2713
   955
        haptic->hwdata = NULL;
slouken@2713
   956
    }
slouken@2713
   957
}
slouken@2713
   958
slouken@2713
   959
slouken@7191
   960
/*
slouken@2713
   961
 * Clean up after system specific haptic stuff
slouken@2713
   962
 */
slouken@2713
   963
void
slouken@2713
   964
SDL_SYS_HapticQuit(void)
slouken@2713
   965
{
icculus@8179
   966
    SDL_hapticlist_item *item;
icculus@8179
   967
    SDL_hapticlist_item *next = NULL;
icculus@8281
   968
    SDL_Haptic *hapticitem = NULL;
slouken@5090
   969
icculus@8281
   970
    extern SDL_Haptic *SDL_haptics;
icculus@8281
   971
    for (hapticitem = SDL_haptics; hapticitem; hapticitem = hapticitem->next) {
icculus@8281
   972
        if ((hapticitem->hwdata->bXInputHaptic) && (hapticitem->hwdata->thread)) {
icculus@8281
   973
            /* we _have_ to stop the thread before we free the XInput DLL! */
icculus@8281
   974
            hapticitem->hwdata->stopThread = 1;
icculus@8281
   975
            SDL_WaitThread(hapticitem->hwdata->thread, NULL);
icculus@8281
   976
            hapticitem->hwdata->thread = NULL;
icculus@8281
   977
        }
icculus@6990
   978
    }
icculus@6990
   979
icculus@8179
   980
    for (item = SDL_hapticlist; item; item = next) {
icculus@8179
   981
        /* Opened and not closed haptics are leaked, this is on purpose.
icculus@8179
   982
         * Close your haptic devices after usage. */
icculus@8281
   983
        /* !!! FIXME: (...is leaking on purpose a good idea?) */
icculus@8179
   984
        next = item->next;
icculus@8179
   985
        SDL_free(item->name);
icculus@8179
   986
        SDL_free(item);
slouken@5090
   987
    }
slouken@5090
   988
icculus@8281
   989
    if (loaded_xinput) {
icculus@8281
   990
        WIN_UnloadXInputDLL();
icculus@8281
   991
        loaded_xinput = SDL_FALSE;
icculus@8281
   992
    }
icculus@8281
   993
icculus@5591
   994
    if (dinput != NULL) {
icculus@6716
   995
        IDirectInput8_Release(dinput);
icculus@5591
   996
        dinput = NULL;
icculus@5591
   997
    }
icculus@5591
   998
icculus@5591
   999
    if (coinitialized) {
icculus@5591
  1000
        WIN_CoUninitialize();
icculus@5591
  1001
        coinitialized = SDL_FALSE;
icculus@5591
  1002
    }
slouken@2713
  1003
}
slouken@2713
  1004
slouken@2713
  1005
slouken@2713
  1006
/*
slouken@2713
  1007
 * Converts an SDL trigger button to an DIEFFECT trigger button.
slouken@2713
  1008
 */
slouken@2713
  1009
static DWORD
slouken@2713
  1010
DIGetTriggerButton(Uint16 button)
slouken@2713
  1011
{
slouken@2713
  1012
    DWORD dwTriggerButton;
slouken@2713
  1013
slouken@2713
  1014
    dwTriggerButton = DIEB_NOTRIGGER;
slouken@2713
  1015
slouken@2713
  1016
    if (button != 0) {
slouken@2713
  1017
        dwTriggerButton = DIJOFS_BUTTON(button - 1);
slouken@2713
  1018
    }
slouken@2713
  1019
slouken@2713
  1020
    return dwTriggerButton;
slouken@2713
  1021
}
slouken@2713
  1022
slouken@2713
  1023
slouken@2713
  1024
/*
slouken@2713
  1025
 * Sets the direction.
slouken@2713
  1026
 */
slouken@2713
  1027
static int
slouken@2713
  1028
SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir, int naxes)
slouken@2713
  1029
{
slouken@2713
  1030
    LONG *rglDir;
slouken@2713
  1031
slouken@2713
  1032
    /* Handle no axes a part. */
slouken@2713
  1033
    if (naxes == 0) {
slouken@2713
  1034
        effect->dwFlags |= DIEFF_SPHERICAL;     /* Set as default. */
slouken@2713
  1035
        effect->rglDirection = NULL;
slouken@2713
  1036
        return 0;
slouken@2713
  1037
    }
slouken@2713
  1038
slouken@2713
  1039
    /* Has axes. */
slouken@2713
  1040
    rglDir = SDL_malloc(sizeof(LONG) * naxes);
slouken@2713
  1041
    if (rglDir == NULL) {
icculus@7037
  1042
        return SDL_OutOfMemory();
slouken@2713
  1043
    }
slouken@2713
  1044
    SDL_memset(rglDir, 0, sizeof(LONG) * naxes);
slouken@2713
  1045
    effect->rglDirection = rglDir;
slouken@2713
  1046
slouken@2713
  1047
    switch (dir->type) {
slouken@2713
  1048
    case SDL_HAPTIC_POLAR:
slouken@2713
  1049
        effect->dwFlags |= DIEFF_POLAR;
slouken@2713
  1050
        rglDir[0] = dir->dir[0];
slouken@2713
  1051
        return 0;
slouken@2713
  1052
    case SDL_HAPTIC_CARTESIAN:
slouken@2713
  1053
        effect->dwFlags |= DIEFF_CARTESIAN;
slouken@2713
  1054
        rglDir[0] = dir->dir[0];
slouken@2713
  1055
        if (naxes > 1)
slouken@2713
  1056
            rglDir[1] = dir->dir[1];
slouken@2713
  1057
        if (naxes > 2)
slouken@2713
  1058
            rglDir[2] = dir->dir[2];
slouken@2713
  1059
        return 0;
slouken@2713
  1060
    case SDL_HAPTIC_SPHERICAL:
slouken@2713
  1061
        effect->dwFlags |= DIEFF_SPHERICAL;
slouken@2713
  1062
        rglDir[0] = dir->dir[0];
slouken@2713
  1063
        if (naxes > 1)
slouken@2713
  1064
            rglDir[1] = dir->dir[1];
slouken@2713
  1065
        if (naxes > 2)
slouken@2713
  1066
            rglDir[2] = dir->dir[2];
slouken@2713
  1067
        return 0;
slouken@2713
  1068
slouken@2713
  1069
    default:
icculus@7037
  1070
        return SDL_SetError("Haptic: Unknown direction type.");
slouken@2713
  1071
    }
slouken@2713
  1072
}
slouken@2713
  1073
slouken@2713
  1074
#define CONVERT(x)   (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF)
slouken@2713
  1075
/*
slouken@2713
  1076
 * Creates the DIEFFECT from a SDL_HapticEffect.
slouken@2713
  1077
 */
slouken@2713
  1078
static int
slouken@2713
  1079
SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest,
slouken@2713
  1080
                   SDL_HapticEffect * src)
slouken@2713
  1081
{
slouken@2713
  1082
    int i;
slouken@2713
  1083
    DICONSTANTFORCE *constant;
slouken@2713
  1084
    DIPERIODIC *periodic;
slouken@2713
  1085
    DICONDITION *condition;     /* Actually an array of conditions - one per axis. */
slouken@2713
  1086
    DIRAMPFORCE *ramp;
slouken@2713
  1087
    DICUSTOMFORCE *custom;
slouken@2713
  1088
    DIENVELOPE *envelope;
slouken@2713
  1089
    SDL_HapticConstant *hap_constant;
slouken@2713
  1090
    SDL_HapticPeriodic *hap_periodic;
slouken@2713
  1091
    SDL_HapticCondition *hap_condition;
slouken@2713
  1092
    SDL_HapticRamp *hap_ramp;
slouken@2713
  1093
    SDL_HapticCustom *hap_custom;
slouken@2713
  1094
    DWORD *axes;
slouken@2713
  1095
slouken@2713
  1096
    /* Set global stuff. */
slouken@2713
  1097
    SDL_memset(dest, 0, sizeof(DIEFFECT));
slouken@2713
  1098
    dest->dwSize = sizeof(DIEFFECT);    /* Set the structure size. */
slouken@2713
  1099
    dest->dwSamplePeriod = 0;   /* Not used by us. */
slouken@2713
  1100
    dest->dwGain = 10000;       /* Gain is set globally, not locally. */
slouken@2713
  1101
    dest->dwFlags = DIEFF_OBJECTOFFSETS;        /* Seems obligatory. */
slouken@2713
  1102
slouken@2713
  1103
    /* Envelope. */
slouken@2713
  1104
    envelope = SDL_malloc(sizeof(DIENVELOPE));
slouken@2713
  1105
    if (envelope == NULL) {
icculus@7037
  1106
        return SDL_OutOfMemory();
slouken@2713
  1107
    }
slouken@2713
  1108
    SDL_memset(envelope, 0, sizeof(DIENVELOPE));
slouken@2713
  1109
    dest->lpEnvelope = envelope;
slouken@2713
  1110
    envelope->dwSize = sizeof(DIENVELOPE);      /* Always should be this. */
slouken@2713
  1111
slouken@2713
  1112
    /* Axes. */
slouken@2713
  1113
    dest->cAxes = haptic->naxes;
slouken@2713
  1114
    if (dest->cAxes > 0) {
slouken@2713
  1115
        axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
slouken@2713
  1116
        if (axes == NULL) {
icculus@7037
  1117
            return SDL_OutOfMemory();
slouken@2713
  1118
        }
slouken@2713
  1119
        axes[0] = haptic->hwdata->axes[0];      /* Always at least one axis. */
slouken@2713
  1120
        if (dest->cAxes > 1) {
slouken@2713
  1121
            axes[1] = haptic->hwdata->axes[1];
slouken@2713
  1122
        }
slouken@2713
  1123
        if (dest->cAxes > 2) {
slouken@2713
  1124
            axes[2] = haptic->hwdata->axes[2];
slouken@2713
  1125
        }
slouken@2713
  1126
        dest->rgdwAxes = axes;
slouken@2713
  1127
    }
slouken@2713
  1128
slouken@2713
  1129
philipp@7132
  1130
    /* The big type handling switch, even bigger then Linux's version. */
slouken@2713
  1131
    switch (src->type) {
slouken@2713
  1132
    case SDL_HAPTIC_CONSTANT:
slouken@2713
  1133
        hap_constant = &src->constant;
slouken@2713
  1134
        constant = SDL_malloc(sizeof(DICONSTANTFORCE));
slouken@2713
  1135
        if (constant == NULL) {
icculus@7037
  1136
            return SDL_OutOfMemory();
slouken@2713
  1137
        }
slouken@2713
  1138
        SDL_memset(constant, 0, sizeof(DICONSTANTFORCE));
slouken@2713
  1139
slouken@2713
  1140
        /* Specifics */
slouken@2713
  1141
        constant->lMagnitude = CONVERT(hap_constant->level);
slouken@2713
  1142
        dest->cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
slouken@2713
  1143
        dest->lpvTypeSpecificParams = constant;
slouken@2713
  1144
slouken@2713
  1145
        /* Generics */
slouken@2713
  1146
        dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */
slouken@2713
  1147
        dest->dwTriggerButton = DIGetTriggerButton(hap_constant->button);
slouken@2713
  1148
        dest->dwTriggerRepeatInterval = hap_constant->interval;
slouken@2713
  1149
        dest->dwStartDelay = hap_constant->delay * 1000;        /* In microseconds. */
slouken@2713
  1150
slouken@2713
  1151
        /* Direction. */
slouken@2713
  1152
        if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes)
slouken@2713
  1153
            < 0) {
slouken@2713
  1154
            return -1;
slouken@2713
  1155
        }
slouken@2713
  1156
slouken@2713
  1157
        /* Envelope */
slouken@2713
  1158
        if ((hap_constant->attack_length == 0)
slouken@2713
  1159
            && (hap_constant->fade_length == 0)) {
slouken@2713
  1160
            SDL_free(dest->lpEnvelope);
slouken@2713
  1161
            dest->lpEnvelope = NULL;
slouken@2713
  1162
        } else {
slouken@2713
  1163
            envelope->dwAttackLevel = CONVERT(hap_constant->attack_level);
slouken@2713
  1164
            envelope->dwAttackTime = hap_constant->attack_length * 1000;
slouken@2713
  1165
            envelope->dwFadeLevel = CONVERT(hap_constant->fade_level);
slouken@2713
  1166
            envelope->dwFadeTime = hap_constant->fade_length * 1000;
slouken@2713
  1167
        }
slouken@2713
  1168
slouken@2713
  1169
        break;
slouken@2713
  1170
slouken@2713
  1171
    case SDL_HAPTIC_SINE:
icculus@7621
  1172
    /* !!! FIXME: put this back when we have more bits in 2.1 */
gabomdq@7678
  1173
    /* case SDL_HAPTIC_SQUARE: */
slouken@2713
  1174
    case SDL_HAPTIC_TRIANGLE:
slouken@2713
  1175
    case SDL_HAPTIC_SAWTOOTHUP:
slouken@2713
  1176
    case SDL_HAPTIC_SAWTOOTHDOWN:
slouken@2713
  1177
        hap_periodic = &src->periodic;
slouken@2713
  1178
        periodic = SDL_malloc(sizeof(DIPERIODIC));
slouken@2713
  1179
        if (periodic == NULL) {
icculus@7037
  1180
            return SDL_OutOfMemory();
slouken@2713
  1181
        }
slouken@2713
  1182
        SDL_memset(periodic, 0, sizeof(DIPERIODIC));
slouken@2713
  1183
slouken@2713
  1184
        /* Specifics */
slouken@2713
  1185
        periodic->dwMagnitude = CONVERT(hap_periodic->magnitude);
slouken@2713
  1186
        periodic->lOffset = CONVERT(hap_periodic->offset);
slouken@2713
  1187
        periodic->dwPhase = hap_periodic->phase;
slouken@2713
  1188
        periodic->dwPeriod = hap_periodic->period * 1000;
slouken@2713
  1189
        dest->cbTypeSpecificParams = sizeof(DIPERIODIC);
slouken@2713
  1190
        dest->lpvTypeSpecificParams = periodic;
slouken@2713
  1191
slouken@2713
  1192
        /* Generics */
slouken@2713
  1193
        dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */
slouken@2713
  1194
        dest->dwTriggerButton = DIGetTriggerButton(hap_periodic->button);
slouken@2713
  1195
        dest->dwTriggerRepeatInterval = hap_periodic->interval;
slouken@2713
  1196
        dest->dwStartDelay = hap_periodic->delay * 1000;        /* In microseconds. */
slouken@2713
  1197
slouken@2713
  1198
        /* Direction. */
slouken@2713
  1199
        if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes)
slouken@2713
  1200
            < 0) {
slouken@2713
  1201
            return -1;
slouken@2713
  1202
        }
slouken@2713
  1203
slouken@2713
  1204
        /* Envelope */
slouken@2713
  1205
        if ((hap_periodic->attack_length == 0)
slouken@2713
  1206
            && (hap_periodic->fade_length == 0)) {
slouken@2713
  1207
            SDL_free(dest->lpEnvelope);
slouken@2713
  1208
            dest->lpEnvelope = NULL;
slouken@2713
  1209
        } else {
slouken@2713
  1210
            envelope->dwAttackLevel = CONVERT(hap_periodic->attack_level);
slouken@2713
  1211
            envelope->dwAttackTime = hap_periodic->attack_length * 1000;
slouken@2713
  1212
            envelope->dwFadeLevel = CONVERT(hap_periodic->fade_level);
slouken@2713
  1213
            envelope->dwFadeTime = hap_periodic->fade_length * 1000;
slouken@2713
  1214
        }
slouken@2713
  1215
slouken@2713
  1216
        break;
slouken@2713
  1217
slouken@2713
  1218
    case SDL_HAPTIC_SPRING:
slouken@2713
  1219
    case SDL_HAPTIC_DAMPER:
slouken@2713
  1220
    case SDL_HAPTIC_INERTIA:
slouken@2713
  1221
    case SDL_HAPTIC_FRICTION:
slouken@2713
  1222
        hap_condition = &src->condition;
slouken@2713
  1223
        condition = SDL_malloc(sizeof(DICONDITION) * dest->cAxes);
slouken@2713
  1224
        if (condition == NULL) {
icculus@7037
  1225
            return SDL_OutOfMemory();
slouken@2713
  1226
        }
slouken@2713
  1227
        SDL_memset(condition, 0, sizeof(DICONDITION));
slouken@2713
  1228
slouken@2713
  1229
        /* Specifics */
slouken@2713
  1230
        for (i = 0; i < (int) dest->cAxes; i++) {
slouken@2713
  1231
            condition[i].lOffset = CONVERT(hap_condition->center[i]);
slouken@2713
  1232
            condition[i].lPositiveCoefficient =
slouken@2713
  1233
                CONVERT(hap_condition->right_coeff[i]);
slouken@2713
  1234
            condition[i].lNegativeCoefficient =
slouken@2713
  1235
                CONVERT(hap_condition->left_coeff[i]);
slouken@2713
  1236
            condition[i].dwPositiveSaturation =
slouken@2713
  1237
                CONVERT(hap_condition->right_sat[i]);
slouken@2713
  1238
            condition[i].dwNegativeSaturation =
slouken@2713
  1239
                CONVERT(hap_condition->left_sat[i]);
slouken@2713
  1240
            condition[i].lDeadBand = CONVERT(hap_condition->deadband[i]);
slouken@2713
  1241
        }
slouken@2713
  1242
        dest->cbTypeSpecificParams = sizeof(DICONDITION) * dest->cAxes;
slouken@2713
  1243
        dest->lpvTypeSpecificParams = condition;
slouken@2713
  1244
slouken@2713
  1245
        /* Generics */
slouken@2713
  1246
        dest->dwDuration = hap_condition->length * 1000;        /* In microseconds. */
slouken@2713
  1247
        dest->dwTriggerButton = DIGetTriggerButton(hap_condition->button);
slouken@2713
  1248
        dest->dwTriggerRepeatInterval = hap_condition->interval;
slouken@2713
  1249
        dest->dwStartDelay = hap_condition->delay * 1000;       /* In microseconds. */
slouken@2713
  1250
slouken@2713
  1251
        /* Direction. */
slouken@2713
  1252
        if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes)
slouken@2713
  1253
            < 0) {
slouken@2713
  1254
            return -1;
slouken@2713
  1255
        }
slouken@2713
  1256
slouken@2713
  1257
        /* Envelope - Not actually supported by most CONDITION implementations. */
slouken@2713
  1258
        SDL_free(dest->lpEnvelope);
slouken@2713
  1259
        dest->lpEnvelope = NULL;
slouken@2713
  1260
slouken@2713
  1261
        break;
slouken@2713
  1262
slouken@2713
  1263
    case SDL_HAPTIC_RAMP:
slouken@2713
  1264
        hap_ramp = &src->ramp;
slouken@2713
  1265
        ramp = SDL_malloc(sizeof(DIRAMPFORCE));
slouken@2713
  1266
        if (ramp == NULL) {
icculus@7037
  1267
            return SDL_OutOfMemory();
slouken@2713
  1268
        }
slouken@2713
  1269
        SDL_memset(ramp, 0, sizeof(DIRAMPFORCE));
slouken@2713
  1270
slouken@2713
  1271
        /* Specifics */
slouken@2713
  1272
        ramp->lStart = CONVERT(hap_ramp->start);
slouken@2713
  1273
        ramp->lEnd = CONVERT(hap_ramp->end);
slouken@2713
  1274
        dest->cbTypeSpecificParams = sizeof(DIRAMPFORCE);
slouken@2713
  1275
        dest->lpvTypeSpecificParams = ramp;
slouken@2713
  1276
slouken@2713
  1277
        /* Generics */
slouken@2713
  1278
        dest->dwDuration = hap_ramp->length * 1000;     /* In microseconds. */
slouken@2713
  1279
        dest->dwTriggerButton = DIGetTriggerButton(hap_ramp->button);
slouken@2713
  1280
        dest->dwTriggerRepeatInterval = hap_ramp->interval;
slouken@2713
  1281
        dest->dwStartDelay = hap_ramp->delay * 1000;    /* In microseconds. */
slouken@2713
  1282
slouken@2713
  1283
        /* Direction. */
slouken@2713
  1284
        if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) {
slouken@2713
  1285
            return -1;
slouken@2713
  1286
        }
slouken@2713
  1287
slouken@2713
  1288
        /* Envelope */
slouken@2713
  1289
        if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) {
slouken@2713
  1290
            SDL_free(dest->lpEnvelope);
slouken@2713
  1291
            dest->lpEnvelope = NULL;
slouken@2713
  1292
        } else {
slouken@2713
  1293
            envelope->dwAttackLevel = CONVERT(hap_ramp->attack_level);
slouken@2713
  1294
            envelope->dwAttackTime = hap_ramp->attack_length * 1000;
slouken@2713
  1295
            envelope->dwFadeLevel = CONVERT(hap_ramp->fade_level);
slouken@2713
  1296
            envelope->dwFadeTime = hap_ramp->fade_length * 1000;
slouken@2713
  1297
        }
slouken@2713
  1298
slouken@2713
  1299
        break;
slouken@2713
  1300
slouken@2713
  1301
    case SDL_HAPTIC_CUSTOM:
slouken@2713
  1302
        hap_custom = &src->custom;
slouken@2713
  1303
        custom = SDL_malloc(sizeof(DICUSTOMFORCE));
slouken@2713
  1304
        if (custom == NULL) {
icculus@7037
  1305
            return SDL_OutOfMemory();
slouken@2713
  1306
        }
slouken@2713
  1307
        SDL_memset(custom, 0, sizeof(DICUSTOMFORCE));
slouken@2713
  1308
slouken@2713
  1309
        /* Specifics */
slouken@2713
  1310
        custom->cChannels = hap_custom->channels;
slouken@2713
  1311
        custom->dwSamplePeriod = hap_custom->period * 1000;
slouken@2713
  1312
        custom->cSamples = hap_custom->samples;
slouken@2713
  1313
        custom->rglForceData =
slouken@2713
  1314
            SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels);
slouken@2713
  1315
        for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) {      /* Copy data. */
slouken@2713
  1316
            custom->rglForceData[i] = CONVERT(hap_custom->data[i]);
slouken@2713
  1317
        }
slouken@2713
  1318
        dest->cbTypeSpecificParams = sizeof(DICUSTOMFORCE);
slouken@2713
  1319
        dest->lpvTypeSpecificParams = custom;
slouken@2713
  1320
slouken@2713
  1321
        /* Generics */
slouken@2713
  1322
        dest->dwDuration = hap_custom->length * 1000;   /* In microseconds. */
slouken@2713
  1323
        dest->dwTriggerButton = DIGetTriggerButton(hap_custom->button);
slouken@2713
  1324
        dest->dwTriggerRepeatInterval = hap_custom->interval;
slouken@2713
  1325
        dest->dwStartDelay = hap_custom->delay * 1000;  /* In microseconds. */
slouken@2713
  1326
slouken@2713
  1327
        /* Direction. */
slouken@2713
  1328
        if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) <
slouken@2713
  1329
            0) {
slouken@2713
  1330
            return -1;
slouken@2713
  1331
        }
slouken@2713
  1332
slouken@2713
  1333
        /* Envelope */
slouken@2713
  1334
        if ((hap_custom->attack_length == 0)
slouken@2713
  1335
            && (hap_custom->fade_length == 0)) {
slouken@2713
  1336
            SDL_free(dest->lpEnvelope);
slouken@2713
  1337
            dest->lpEnvelope = NULL;
slouken@2713
  1338
        } else {
slouken@2713
  1339
            envelope->dwAttackLevel = CONVERT(hap_custom->attack_level);
slouken@2713
  1340
            envelope->dwAttackTime = hap_custom->attack_length * 1000;
slouken@2713
  1341
            envelope->dwFadeLevel = CONVERT(hap_custom->fade_level);
slouken@2713
  1342
            envelope->dwFadeTime = hap_custom->fade_length * 1000;
slouken@2713
  1343
        }
slouken@2713
  1344
slouken@2713
  1345
        break;
slouken@2713
  1346
slouken@2713
  1347
slouken@2713
  1348
    default:
icculus@7037
  1349
        return SDL_SetError("Haptic: Unknown effect type.");
slouken@2713
  1350
    }
slouken@2713
  1351
slouken@2713
  1352
    return 0;
slouken@2713
  1353
}
slouken@2713
  1354
slouken@2713
  1355
slouken@2713
  1356
/*
slouken@2713
  1357
 * Frees an DIEFFECT allocated by SDL_SYS_ToDIEFFECT.
slouken@2713
  1358
 */
slouken@2713
  1359
static void
slouken@2713
  1360
SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type)
slouken@2713
  1361
{
slouken@2713
  1362
    DICUSTOMFORCE *custom;
slouken@2713
  1363
slouken@7719
  1364
    SDL_free(effect->lpEnvelope);
slouken@7719
  1365
    effect->lpEnvelope = NULL;
slouken@7719
  1366
    SDL_free(effect->rgdwAxes);
slouken@7719
  1367
    effect->rgdwAxes = NULL;
slouken@2713
  1368
    if (effect->lpvTypeSpecificParams != NULL) {
slouken@2713
  1369
        if (type == SDL_HAPTIC_CUSTOM) {        /* Must free the custom data. */
slouken@2713
  1370
            custom = (DICUSTOMFORCE *) effect->lpvTypeSpecificParams;
slouken@2713
  1371
            SDL_free(custom->rglForceData);
slouken@2713
  1372
            custom->rglForceData = NULL;
slouken@2713
  1373
        }
slouken@2713
  1374
        SDL_free(effect->lpvTypeSpecificParams);
slouken@2713
  1375
        effect->lpvTypeSpecificParams = NULL;
slouken@2713
  1376
    }
slouken@7719
  1377
    SDL_free(effect->rglDirection);
slouken@7719
  1378
    effect->rglDirection = NULL;
slouken@2713
  1379
}
slouken@2713
  1380
slouken@2713
  1381
slouken@2713
  1382
/*
slouken@2713
  1383
 * Gets the effect type from the generic SDL haptic effect wrapper.
slouken@2713
  1384
 */
slouken@2713
  1385
static REFGUID
slouken@2713
  1386
SDL_SYS_HapticEffectType(SDL_HapticEffect * effect)
slouken@2713
  1387
{
slouken@2713
  1388
    switch (effect->type) {
slouken@2713
  1389
    case SDL_HAPTIC_CONSTANT:
slouken@2713
  1390
        return &GUID_ConstantForce;
slouken@2713
  1391
slouken@2713
  1392
    case SDL_HAPTIC_RAMP:
slouken@2713
  1393
        return &GUID_RampForce;
slouken@2713
  1394
icculus@7621
  1395
    /* !!! FIXME: put this back when we have more bits in 2.1 */
gabomdq@7678
  1396
    /* case SDL_HAPTIC_SQUARE:
gabomdq@7677
  1397
        return &GUID_Square; */
slouken@2713
  1398
slouken@2713
  1399
    case SDL_HAPTIC_SINE:
slouken@2713
  1400
        return &GUID_Sine;
slouken@2713
  1401
slouken@2713
  1402
    case SDL_HAPTIC_TRIANGLE:
slouken@2713
  1403
        return &GUID_Triangle;
slouken@2713
  1404
slouken@2713
  1405
    case SDL_HAPTIC_SAWTOOTHUP:
slouken@2713
  1406
        return &GUID_SawtoothUp;
slouken@2713
  1407
slouken@2713
  1408
    case SDL_HAPTIC_SAWTOOTHDOWN:
slouken@2713
  1409
        return &GUID_SawtoothDown;
slouken@2713
  1410
slouken@2713
  1411
    case SDL_HAPTIC_SPRING:
slouken@2713
  1412
        return &GUID_Spring;
slouken@2713
  1413
slouken@2713
  1414
    case SDL_HAPTIC_DAMPER:
slouken@2713
  1415
        return &GUID_Damper;
slouken@2713
  1416
slouken@2713
  1417
    case SDL_HAPTIC_INERTIA:
slouken@2713
  1418
        return &GUID_Inertia;
slouken@2713
  1419
slouken@2713
  1420
    case SDL_HAPTIC_FRICTION:
slouken@2713
  1421
        return &GUID_Friction;
slouken@2713
  1422
slouken@2713
  1423
    case SDL_HAPTIC_CUSTOM:
slouken@2713
  1424
        return &GUID_CustomForce;
slouken@2713
  1425
slouken@2713
  1426
    default:
slouken@2713
  1427
        return NULL;
slouken@2713
  1428
    }
slouken@2713
  1429
}
slouken@2713
  1430
slouken@2713
  1431
slouken@2713
  1432
/*
slouken@2713
  1433
 * Creates a new haptic effect.
slouken@2713
  1434
 */
slouken@2713
  1435
int
slouken@2713
  1436
SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
slouken@2713
  1437
                        SDL_HapticEffect * base)
slouken@2713
  1438
{
slouken@2713
  1439
    HRESULT ret;
icculus@7850
  1440
    REFGUID type = SDL_SYS_HapticEffectType(base);
slouken@2713
  1441
icculus@7850
  1442
    if ((type == NULL) && (!haptic->hwdata->bXInputHaptic)) {
icculus@7884
  1443
        SDL_SetError("Haptic: Unknown effect type.");
icculus@7850
  1444
        goto err_hweffect;
slouken@2713
  1445
    }
slouken@2713
  1446
slouken@2713
  1447
    /* Alloc the effect. */
slouken@2713
  1448
    effect->hweffect = (struct haptic_hweffect *)
slouken@2713
  1449
        SDL_malloc(sizeof(struct haptic_hweffect));
slouken@2713
  1450
    if (effect->hweffect == NULL) {
slouken@2713
  1451
        SDL_OutOfMemory();
slouken@2713
  1452
        goto err_hweffect;
slouken@2713
  1453
    }
slouken@2713
  1454
icculus@6990
  1455
    SDL_zerop(effect->hweffect);
icculus@6990
  1456
icculus@6990
  1457
    if (haptic->hwdata->bXInputHaptic) {
icculus@7621
  1458
        SDL_assert(base->type == SDL_HAPTIC_LEFTRIGHT);  /* should catch this at higher level */
icculus@6990
  1459
        return SDL_SYS_HapticUpdateEffect(haptic, effect, base);
icculus@6990
  1460
    }
icculus@6990
  1461
slouken@2713
  1462
    /* Get the effect. */
slouken@2713
  1463
    if (SDL_SYS_ToDIEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
slouken@2713
  1464
        goto err_effectdone;
slouken@2713
  1465
    }
slouken@2713
  1466
slouken@2713
  1467
    /* Create the actual effect. */
icculus@6716
  1468
    ret = IDirectInputDevice8_CreateEffect(haptic->hwdata->device, type,
slouken@2713
  1469
                                           &effect->hweffect->effect,
slouken@2713
  1470
                                           &effect->hweffect->ref, NULL);
slouken@2713
  1471
    if (FAILED(ret)) {
slouken@2713
  1472
        DI_SetError("Unable to create effect", ret);
slouken@2713
  1473
        goto err_effectdone;
slouken@2713
  1474
    }
slouken@2713
  1475
slouken@2713
  1476
    return 0;
slouken@2713
  1477
slouken@2713
  1478
  err_effectdone:
slouken@2713
  1479
    SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, base->type);
slouken@2713
  1480
  err_hweffect:
slouken@7719
  1481
    SDL_free(effect->hweffect);
slouken@7719
  1482
    effect->hweffect = NULL;
slouken@2713
  1483
    return -1;
slouken@2713
  1484
}
slouken@2713
  1485
slouken@2713
  1486
slouken@2713
  1487
/*
slouken@2713
  1488
 * Updates an effect.
slouken@2713
  1489
 */
slouken@2713
  1490
int
slouken@2713
  1491
SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
slouken@2713
  1492
                           struct haptic_effect *effect,
slouken@2713
  1493
                           SDL_HapticEffect * data)
slouken@2713
  1494
{
slouken@2713
  1495
    HRESULT ret;
slouken@2713
  1496
    DWORD flags;
slouken@2713
  1497
    DIEFFECT temp;
slouken@2713
  1498
icculus@6990
  1499
    if (haptic->hwdata->bXInputHaptic) {
icculus@6990
  1500
        XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
icculus@7621
  1501
        SDL_assert(data->type == SDL_HAPTIC_LEFTRIGHT);
icculus@7621
  1502
        vib->wLeftMotorSpeed = data->leftright.large_magnitude;
icculus@7621
  1503
        vib->wRightMotorSpeed = data->leftright.small_magnitude;
icculus@7623
  1504
        SDL_LockMutex(haptic->hwdata->mutex);
icculus@7623
  1505
        if (haptic->hwdata->stopTicks) {  /* running right now? Update it. */
icculus@7623
  1506
            XINPUTSETSTATE(haptic->hwdata->userid, vib);
icculus@7623
  1507
        }
icculus@7623
  1508
        SDL_UnlockMutex(haptic->hwdata->mutex);
icculus@6990
  1509
        return 0;
icculus@6990
  1510
    }
icculus@6990
  1511
slouken@2713
  1512
    /* Get the effect. */
slouken@2713
  1513
    SDL_memset(&temp, 0, sizeof(DIEFFECT));
slouken@2713
  1514
    if (SDL_SYS_ToDIEFFECT(haptic, &temp, data) < 0) {
slouken@2713
  1515
        goto err_update;
slouken@2713
  1516
    }
slouken@2713
  1517
slouken@2713
  1518
    /* Set the flags.  Might be worthwhile to diff temp with loaded effect and
slouken@2713
  1519
     *  only change those parameters. */
slouken@2713
  1520
    flags = DIEP_DIRECTION |
slouken@2713
  1521
        DIEP_DURATION |
slouken@2713
  1522
        DIEP_ENVELOPE |
slouken@2713
  1523
        DIEP_STARTDELAY |
slouken@2713
  1524
        DIEP_TRIGGERBUTTON |
slouken@2713
  1525
        DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS;
slouken@2713
  1526
slouken@2713
  1527
    /* Create the actual effect. */
slouken@2713
  1528
    ret =
slouken@2713
  1529
        IDirectInputEffect_SetParameters(effect->hweffect->ref, &temp, flags);
slouken@2713
  1530
    if (FAILED(ret)) {
slouken@2713
  1531
        DI_SetError("Unable to update effect", ret);
slouken@2713
  1532
        goto err_update;
slouken@2713
  1533
    }
slouken@2713
  1534
slouken@2713
  1535
    /* Copy it over. */
slouken@2713
  1536
    SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, data->type);
slouken@2713
  1537
    SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(DIEFFECT));
slouken@2713
  1538
slouken@2713
  1539
    return 0;
slouken@2713
  1540
slouken@2713
  1541
  err_update:
slouken@2713
  1542
    SDL_SYS_HapticFreeDIEFFECT(&temp, data->type);
slouken@2713
  1543
    return -1;
slouken@2713
  1544
}
slouken@2713
  1545
slouken@2713
  1546
slouken@2713
  1547
/*
slouken@2713
  1548
 * Runs an effect.
slouken@2713
  1549
 */
slouken@2713
  1550
int
slouken@2713
  1551
SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
slouken@2713
  1552
                        Uint32 iterations)
slouken@2713
  1553
{
slouken@2713
  1554
    HRESULT ret;
slouken@2713
  1555
    DWORD iter;
slouken@2713
  1556
icculus@6990
  1557
    if (haptic->hwdata->bXInputHaptic) {
icculus@6990
  1558
        XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
icculus@7621
  1559
        SDL_assert(effect->effect.type == SDL_HAPTIC_LEFTRIGHT);  /* should catch this at higher level */
icculus@7481
  1560
        SDL_LockMutex(haptic->hwdata->mutex);
icculus@7847
  1561
        if(effect->effect.leftright.length == SDL_HAPTIC_INFINITY || iterations == SDL_HAPTIC_INFINITY) {
icculus@7847
  1562
            haptic->hwdata->stopTicks = SDL_HAPTIC_INFINITY;
icculus@7855
  1563
        } else if ((!effect->effect.leftright.length) || (!iterations)) {
icculus@7855
  1564
            /* do nothing. Effect runs for zero milliseconds. */
icculus@7847
  1565
        } else {
icculus@7847
  1566
            haptic->hwdata->stopTicks = SDL_GetTicks() + (effect->effect.leftright.length * iterations);
icculus@7855
  1567
            if ((haptic->hwdata->stopTicks == SDL_HAPTIC_INFINITY) || (haptic->hwdata->stopTicks == 0)) {
icculus@7855
  1568
                haptic->hwdata->stopTicks = 1;  /* fix edge cases. */
icculus@7855
  1569
            }
icculus@7847
  1570
        }
icculus@7481
  1571
        SDL_UnlockMutex(haptic->hwdata->mutex);
icculus@7481
  1572
        return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS) ? 0 : -1;
icculus@6990
  1573
    }
icculus@6990
  1574
slouken@2713
  1575
    /* Check if it's infinite. */
slouken@2713
  1576
    if (iterations == SDL_HAPTIC_INFINITY) {
slouken@2713
  1577
        iter = INFINITE;
slouken@2713
  1578
    } else
slouken@2713
  1579
        iter = iterations;
slouken@2713
  1580
slouken@2713
  1581
    /* Run the effect. */
slouken@2713
  1582
    ret = IDirectInputEffect_Start(effect->hweffect->ref, iter, 0);
slouken@2713
  1583
    if (FAILED(ret)) {
icculus@7037
  1584
        return DI_SetError("Running the effect", ret);
slouken@2713
  1585
    }
slouken@2713
  1586
slouken@2713
  1587
    return 0;
slouken@2713
  1588
}
slouken@2713
  1589
slouken@2713
  1590
slouken@2713
  1591
/*
slouken@2713
  1592
 * Stops an effect.
slouken@2713
  1593
 */
slouken@2713
  1594
int
slouken@2713
  1595
SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
slouken@2713
  1596
{
slouken@2713
  1597
    HRESULT ret;
slouken@2713
  1598
icculus@6990
  1599
    if (haptic->hwdata->bXInputHaptic) {
icculus@6990
  1600
        XINPUT_VIBRATION vibration = { 0, 0 };
icculus@7481
  1601
        SDL_LockMutex(haptic->hwdata->mutex);
icculus@7481
  1602
        haptic->hwdata->stopTicks = 0;
icculus@7481
  1603
        SDL_UnlockMutex(haptic->hwdata->mutex);
icculus@7481
  1604
        return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
icculus@6990
  1605
    }
icculus@6990
  1606
slouken@2713
  1607
    ret = IDirectInputEffect_Stop(effect->hweffect->ref);
slouken@2713
  1608
    if (FAILED(ret)) {
icculus@7037
  1609
        return DI_SetError("Unable to stop effect", ret);
slouken@2713
  1610
    }
slouken@2713
  1611
slouken@2713
  1612
    return 0;
slouken@2713
  1613
}
slouken@2713
  1614
slouken@2713
  1615
slouken@2713
  1616
/*
slouken@2713
  1617
 * Frees the effect.
slouken@2713
  1618
 */
slouken@2713
  1619
void
slouken@2713
  1620
SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
slouken@2713
  1621
{
slouken@2713
  1622
    HRESULT ret;
slouken@2713
  1623
icculus@6990
  1624
    if (haptic->hwdata->bXInputHaptic) {
icculus@6990
  1625
        SDL_SYS_HapticStopEffect(haptic, effect);
icculus@6990
  1626
    } else {
icculus@6990
  1627
        ret = IDirectInputEffect_Unload(effect->hweffect->ref);
icculus@6990
  1628
        if (FAILED(ret)) {
icculus@6990
  1629
            DI_SetError("Removing effect from the device", ret);
icculus@6990
  1630
        }
icculus@6990
  1631
        SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect,
icculus@6990
  1632
                                   effect->effect.type);
slouken@2713
  1633
    }
slouken@2713
  1634
    SDL_free(effect->hweffect);
slouken@2713
  1635
    effect->hweffect = NULL;
slouken@2713
  1636
}
slouken@2713
  1637
slouken@2713
  1638
slouken@2713
  1639
/*
slouken@2713
  1640
 * Gets the status of a haptic effect.
slouken@2713
  1641
 */
slouken@2713
  1642
int
slouken@2713
  1643
SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
slouken@2713
  1644
                              struct haptic_effect *effect)
slouken@2713
  1645
{
slouken@2713
  1646
    HRESULT ret;
slouken@2713
  1647
    DWORD status;
slouken@2713
  1648
slouken@2713
  1649
    ret = IDirectInputEffect_GetEffectStatus(effect->hweffect->ref, &status);
slouken@2713
  1650
    if (FAILED(ret)) {
icculus@7037
  1651
        return DI_SetError("Getting effect status", ret);
slouken@2713
  1652
    }
slouken@2713
  1653
slouken@2713
  1654
    if (status == 0)
slouken@2713
  1655
        return SDL_FALSE;
slouken@2713
  1656
    return SDL_TRUE;
slouken@2713
  1657
}
slouken@2713
  1658
slouken@2713
  1659
slouken@2713
  1660
/*
slouken@2713
  1661
 * Sets the gain.
slouken@2713
  1662
 */
slouken@2713
  1663
int
slouken@2713
  1664
SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
slouken@2713
  1665
{
slouken@2713
  1666
    HRESULT ret;
slouken@2713
  1667
    DIPROPDWORD dipdw;
slouken@2713
  1668
slouken@2713
  1669
    /* Create the weird structure thingy. */
slouken@2713
  1670
    dipdw.diph.dwSize = sizeof(DIPROPDWORD);
slouken@2713
  1671
    dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
slouken@2713
  1672
    dipdw.diph.dwObj = 0;
slouken@2713
  1673
    dipdw.diph.dwHow = DIPH_DEVICE;
slouken@2713
  1674
    dipdw.dwData = gain * 100;  /* 0 to 10,000 */
slouken@2713
  1675
slouken@2713
  1676
    /* Try to set the autocenter. */
icculus@6716
  1677
    ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
slouken@2713
  1678
                                          DIPROP_FFGAIN, &dipdw.diph);
slouken@2713
  1679
    if (FAILED(ret)) {
icculus@7037
  1680
        return DI_SetError("Setting gain", ret);
slouken@2713
  1681
    }
slouken@2713
  1682
slouken@2713
  1683
    return 0;
slouken@2713
  1684
}
slouken@2713
  1685
slouken@2713
  1686
slouken@2713
  1687
/*
slouken@2713
  1688
 * Sets the autocentering.
slouken@2713
  1689
 */
slouken@2713
  1690
int
slouken@2713
  1691
SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
slouken@2713
  1692
{
slouken@2713
  1693
    HRESULT ret;
slouken@2713
  1694
    DIPROPDWORD dipdw;
slouken@2713
  1695
slouken@2713
  1696
    /* Create the weird structure thingy. */
slouken@2713
  1697
    dipdw.diph.dwSize = sizeof(DIPROPDWORD);
slouken@2713
  1698
    dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
slouken@2713
  1699
    dipdw.diph.dwObj = 0;
slouken@2713
  1700
    dipdw.diph.dwHow = DIPH_DEVICE;
slouken@2713
  1701
    dipdw.dwData = (autocenter == 0) ? DIPROPAUTOCENTER_OFF :
slouken@2713
  1702
        DIPROPAUTOCENTER_ON;
slouken@2713
  1703
slouken@2713
  1704
    /* Try to set the autocenter. */
icculus@6716
  1705
    ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
slouken@2713
  1706
                                          DIPROP_AUTOCENTER, &dipdw.diph);
slouken@2713
  1707
    if (FAILED(ret)) {
icculus@7037
  1708
        return DI_SetError("Setting autocenter", ret);
slouken@2713
  1709
    }
slouken@2713
  1710
slouken@2713
  1711
    return 0;
slouken@2713
  1712
}
slouken@2713
  1713
slouken@2713
  1714
slouken@2713
  1715
/*
slouken@2713
  1716
 * Pauses the device.
slouken@2713
  1717
 */
slouken@2713
  1718
int
slouken@2713
  1719
SDL_SYS_HapticPause(SDL_Haptic * haptic)
slouken@2713
  1720
{
slouken@2713
  1721
    HRESULT ret;
slouken@2713
  1722
slouken@2713
  1723
    /* Pause the device. */
icculus@6716
  1724
    ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
slouken@2713
  1725
                                                       DISFFC_PAUSE);
slouken@2713
  1726
    if (FAILED(ret)) {
icculus@7037
  1727
        return DI_SetError("Pausing the device", ret);
slouken@2713
  1728
    }
slouken@2713
  1729
slouken@2713
  1730
    return 0;
slouken@2713
  1731
}
slouken@2713
  1732
slouken@2713
  1733
slouken@2713
  1734
/*
slouken@2713
  1735
 * Pauses the device.
slouken@2713
  1736
 */
slouken@2713
  1737
int
slouken@2713
  1738
SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
slouken@2713
  1739
{
slouken@2713
  1740
    HRESULT ret;
slouken@2713
  1741
slouken@2713
  1742
    /* Unpause the device. */
icculus@6716
  1743
    ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
slouken@2713
  1744
                                                       DISFFC_CONTINUE);
slouken@2713
  1745
    if (FAILED(ret)) {
icculus@7037
  1746
        return DI_SetError("Pausing the device", ret);
slouken@2713
  1747
    }
slouken@2713
  1748
slouken@2713
  1749
    return 0;
slouken@2713
  1750
}
slouken@2713
  1751
slouken@2713
  1752
slouken@2713
  1753
/*
slouken@2713
  1754
 * Stops all the playing effects on the device.
slouken@2713
  1755
 */
slouken@2713
  1756
int
bobbens@2715
  1757
SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
slouken@2713
  1758
{
slouken@2713
  1759
    HRESULT ret;
slouken@2713
  1760
icculus@6990
  1761
    if (haptic->hwdata->bXInputHaptic) {
icculus@6990
  1762
        XINPUT_VIBRATION vibration = { 0, 0 };
icculus@7481
  1763
        SDL_LockMutex(haptic->hwdata->mutex);
icculus@7481
  1764
        haptic->hwdata->stopTicks = 0;
icculus@7481
  1765
        SDL_UnlockMutex(haptic->hwdata->mutex);
icculus@7481
  1766
        return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
icculus@6990
  1767
    }
icculus@6990
  1768
slouken@2713
  1769
    /* Try to stop the effects. */
icculus@6716
  1770
    ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
slouken@2713
  1771
                                                       DISFFC_STOPALL);
slouken@2713
  1772
    if (FAILED(ret)) {
icculus@7037
  1773
        return DI_SetError("Stopping the device", ret);
slouken@2713
  1774
    }
slouken@2713
  1775
slouken@2713
  1776
    return 0;
slouken@2713
  1777
}
slouken@2713
  1778
icculus@7481
  1779
icculus@7481
  1780
/* !!! FIXME: this is a hack, remove this later. */
icculus@7481
  1781
/* Since XInput doesn't offer a way to vibrate for X time, we hook into
icculus@7481
  1782
 *  SDL_PumpEvents() to check if it's time to stop vibrating with some
icculus@7481
  1783
 *  frequency.
icculus@7481
  1784
 * In practice, this works for 99% of use cases. But in an ideal world,
icculus@7481
  1785
 *  we do this in a separate thread so that:
icculus@7481
  1786
 *    - we aren't bound to when the app chooses to pump the event queue.
icculus@7481
  1787
 *    - we aren't adding more polling to the event queue
icculus@7481
  1788
 *    - we can emulate all the haptic effects correctly (start on a delay,
icculus@7481
  1789
 *      mix multiple effects, etc).
icculus@7481
  1790
 *
icculus@7481
  1791
 * Mostly, this is here to get rumbling to work, and all the other features
icculus@7481
  1792
 *  are absent in the XInput path for now.  :(
icculus@7481
  1793
 */
icculus@7481
  1794
static int SDLCALL
icculus@7481
  1795
SDL_RunXInputHaptic(void *arg)
icculus@7481
  1796
{
icculus@7481
  1797
    struct haptic_hwdata *hwdata = (struct haptic_hwdata *) arg;
icculus@7481
  1798
icculus@7481
  1799
    while (!hwdata->stopThread) {
icculus@7481
  1800
        SDL_Delay(50);
icculus@7481
  1801
        SDL_LockMutex(hwdata->mutex);
icculus@7481
  1802
        /* If we're currently running and need to stop... */
icculus@7849
  1803
        if (hwdata->stopTicks) {
slouken@7858
  1804
            if ((hwdata->stopTicks != SDL_HAPTIC_INFINITY) && SDL_TICKS_PASSED(SDL_GetTicks(), hwdata->stopTicks)) {
icculus@7847
  1805
                XINPUT_VIBRATION vibration = { 0, 0 };
icculus@7847
  1806
                hwdata->stopTicks = 0;
icculus@7847
  1807
                XINPUTSETSTATE(hwdata->userid, &vibration);
icculus@7847
  1808
            }
icculus@7481
  1809
        }
icculus@7481
  1810
        SDL_UnlockMutex(hwdata->mutex);
icculus@7481
  1811
    }
icculus@7481
  1812
icculus@7481
  1813
    return 0;
icculus@7481
  1814
}
icculus@7481
  1815
slouken@7191
  1816
#endif /* SDL_HAPTIC_DINPUT */
slouken@2713
  1817
slouken@7191
  1818
/* vi: set ts=4 sw=4 expandtab: */