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