src/joystick/winrt/SDL_xinputjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 13 Mar 2014 00:40:08 -0700
changeset 8615 097646deaef2
parent 8609 d59f0f12bf07
child 8860 c4133d635375
permissions -rw-r--r--
Fixed the copyright date on files contributed by David Ludwig
dludwig@8471
     1
/*
dludwig@8471
     2
  Simple DirectMedia Layer
slouken@8615
     3
  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
dludwig@8471
     4
dludwig@8471
     5
  This software is provided 'as-is', without any express or implied
dludwig@8471
     6
  warranty.  In no event will the authors be held liable for any damages
dludwig@8471
     7
  arising from the use of this software.
dludwig@8471
     8
dludwig@8471
     9
  Permission is granted to anyone to use this software for any purpose,
dludwig@8471
    10
  including commercial applications, and to alter it and redistribute it
dludwig@8471
    11
  freely, subject to the following restrictions:
dludwig@8471
    12
dludwig@8471
    13
  1. The origin of this software must not be misrepresented; you must not
dludwig@8471
    14
     claim that you wrote the original software. If you use this software
dludwig@8471
    15
     in a product, an acknowledgment in the product documentation would be
dludwig@8471
    16
     appreciated but is not required.
dludwig@8471
    17
  2. Altered source versions must be plainly marked as such, and must not be
dludwig@8471
    18
     misrepresented as being the original software.
dludwig@8471
    19
  3. This notice may not be removed or altered from any source distribution.
dludwig@8471
    20
*/
slouken@8609
    21
#include "../../SDL_internal.h"
dludwig@8471
    22
dludwig@8471
    23
#if SDL_JOYSTICK_XINPUT
dludwig@8471
    24
dludwig@8471
    25
/* SDL_xinputjoystick.c implements an XInput-only joystick and game controller
dludwig@8471
    26
   backend that is suitable for use on WinRT.  SDL's DirectInput backend, also
dludwig@8471
    27
   XInput-capable, was not used as DirectInput is not available on WinRT (or,
dludwig@8471
    28
   at least, it isn't a public API).  Some portions of this XInput backend
dludwig@8471
    29
   may copy parts of the XInput-using code from the DirectInput backend.
dludwig@8471
    30
   Refactoring the common parts into one location may be good to-do at some
dludwig@8471
    31
   point.
dludwig@8471
    32
dludwig@8471
    33
   TODO, WinRT: add hotplug support for XInput based game controllers
dludwig@8471
    34
*/
dludwig@8471
    35
dludwig@8471
    36
#include "SDL_joystick.h"
dludwig@8471
    37
#include "../SDL_sysjoystick.h"
dludwig@8471
    38
#include "../SDL_joystick_c.h"
dludwig@8471
    39
#include "SDL_events.h"
dludwig@8473
    40
#include "../../events/SDL_events_c.h"
dludwig@8552
    41
#include "SDL_timer.h"
dludwig@8471
    42
dludwig@8471
    43
#include <Windows.h>
dludwig@8471
    44
#include <Xinput.h>
dludwig@8471
    45
dludwig@8471
    46
struct joystick_hwdata {
dludwig@8552
    47
    //Uint8 bXInputHaptic; // Supports force feedback via XInput.
dludwig@8552
    48
    DWORD userIndex;    // The XInput device index, in the range [0, XUSER_MAX_COUNT-1] (probably [0,3]).
dludwig@8552
    49
    XINPUT_STATE XInputState;   // the last-read in XInputState, kept around to compare old and new values
dludwig@8552
    50
    SDL_bool isDeviceConnected; // was the device connected (on the last detection-polling, or during backend-initialization)?
dludwig@8552
    51
    SDL_bool isDeviceConnectionEventPending;    // was a device added, and is the associated add-event pending?
dludwig@8552
    52
    SDL_bool isDeviceRemovalEventPending;   // was the device removed, and is the associated remove-event pending?
dludwig@8471
    53
};
dludwig@8471
    54
dludwig@8471
    55
/* Keep track of data on all XInput devices, regardless of whether or not
dludwig@8471
    56
   they've been opened (via SDL_JoystickOpen).
dludwig@8471
    57
 */
dludwig@8471
    58
static struct joystick_hwdata g_XInputData[XUSER_MAX_COUNT];
dludwig@8471
    59
dludwig@8552
    60
/* Device detection can be extremely costly performance-wise, in some cases.
dludwig@8552
    61
   In particular, if no devices are connected, calls to detect a single device,
dludwig@8552
    62
   via either XInputGetState() or XInputGetCapabilities(), can take upwards of
dludwig@8552
    63
   20 ms on a 1st generation Surface RT, more if devices are detected across
dludwig@8552
    64
   all of of XInput's four device slots.  WinRT and XInput do not appear to
dludwig@8552
    65
   have callback-based APIs to notify an app when a device is connected, at
dludwig@8552
    66
   least as of Windows 8.1.  The synchronous XInput calls must be used.
dludwig@8552
    67
dludwig@8552
    68
   Once a device is connected, calling XInputGetState() is a much less costly
dludwig@8552
    69
   operation, with individual calls costing well under 1 ms, and often under
dludwig@8552
    70
   0.1 ms [on a 1st gen Surface RT].
dludwig@8552
    71
dludwig@8552
    72
   With XInput's performance limitations in mind, a separate device-detection
dludwig@8552
    73
   thread will be utilized (by SDL) to try to move costly XInput calls off the
dludwig@8552
    74
   main thread.  Polling of active devices still, however, occurs on the main
dludwig@8552
    75
   thread.
dludwig@8552
    76
 */
dludwig@8552
    77
static SDL_Thread * g_DeviceDetectionThread = NULL;
dludwig@8552
    78
static SDL_mutex * g_DeviceInfoLock = NULL;
dludwig@8552
    79
static SDL_bool g_DeviceDetectionQuit = SDL_FALSE;
dludwig@8552
    80
dludwig@8552
    81
/* Main function for the device-detection thread.
dludwig@8552
    82
 */
dludwig@8552
    83
static int
dludwig@8552
    84
DeviceDetectionThreadMain(void * _data)
dludwig@8552
    85
{
dludwig@8552
    86
    DWORD result;
dludwig@8552
    87
    XINPUT_CAPABILITIES tempXInputCaps;
dludwig@8552
    88
    int i;
dludwig@8552
    89
dludwig@8552
    90
    while (1) {
dludwig@8552
    91
        /* See if the device-detection thread is being asked to shutdown.
dludwig@8552
    92
         */
dludwig@8552
    93
        SDL_LockMutex(g_DeviceInfoLock);
dludwig@8552
    94
        if (g_DeviceDetectionQuit) {
dludwig@8552
    95
            SDL_UnlockMutex(g_DeviceInfoLock);
dludwig@8552
    96
            break;
dludwig@8552
    97
        }
dludwig@8552
    98
        SDL_UnlockMutex(g_DeviceInfoLock);
dludwig@8552
    99
dludwig@8552
   100
        /* Add a short delay to prevent the device-detection thread from eating
dludwig@8552
   101
           up too much CPU time:
dludwig@8552
   102
         */
dludwig@8552
   103
        SDL_Delay(300);
dludwig@8552
   104
dludwig@8552
   105
        /* TODO, WinRT: try making the device-detection thread wakeup sooner from its CPU-preserving SDL_Delay, if the thread was asked to quit.
dludwig@8552
   106
         */
dludwig@8552
   107
dludwig@8552
   108
        /* See if any new devices are connected. */
dludwig@8562
   109
        SDL_LockMutex(g_DeviceInfoLock);
dludwig@8552
   110
        for (i = 0; i < XUSER_MAX_COUNT; ++i) {
dludwig@8552
   111
            if (!g_XInputData[i].isDeviceConnected &&
dludwig@8552
   112
                !g_XInputData[i].isDeviceConnectionEventPending &&
dludwig@8552
   113
                !g_XInputData[i].isDeviceRemovalEventPending)
dludwig@8552
   114
            {
dludwig@8562
   115
                SDL_UnlockMutex(g_DeviceInfoLock);
dludwig@8552
   116
                result = XInputGetCapabilities(i, 0, &tempXInputCaps);
dludwig@8562
   117
                SDL_LockMutex(g_DeviceInfoLock);
dludwig@8552
   118
                if (result == ERROR_SUCCESS) {
dludwig@8552
   119
                    /* Yes, a device is connected.  Mark it as such.
dludwig@8552
   120
                       Others will be told about this (via an
dludwig@8552
   121
                       SDL_JOYDEVICEADDED event) in the next call to
dludwig@8552
   122
                       SDL_SYS_JoystickDetect.
dludwig@8552
   123
                     */
dludwig@8552
   124
                    g_XInputData[i].isDeviceConnected = SDL_TRUE;
dludwig@8552
   125
                    g_XInputData[i].isDeviceConnectionEventPending = SDL_TRUE;
dludwig@8552
   126
                }
dludwig@8552
   127
            }
dludwig@8552
   128
        }
dludwig@8562
   129
        SDL_UnlockMutex(g_DeviceInfoLock);
dludwig@8552
   130
    }
dludwig@8552
   131
dludwig@8552
   132
    return 0;
dludwig@8552
   133
}
dludwig@8552
   134
dludwig@8471
   135
/* Function to scan the system for joysticks.
dludwig@8471
   136
 * It should return 0, or -1 on an unrecoverable fatal error.
dludwig@8471
   137
 */
dludwig@8471
   138
int
dludwig@8471
   139
SDL_SYS_JoystickInit(void)
dludwig@8471
   140
{
dludwig@8471
   141
    HRESULT result = S_OK;
dludwig@8471
   142
    XINPUT_STATE tempXInputState;
dludwig@8471
   143
    int i;
dludwig@8471
   144
dludwig@8471
   145
    SDL_zero(g_XInputData);
dludwig@8471
   146
dludwig@8471
   147
    /* Make initial notes on whether or not devices are connected (or not).
dludwig@8471
   148
     */
dludwig@8471
   149
    for (i = 0; i < XUSER_MAX_COUNT; ++i) {
dludwig@8471
   150
        result = XInputGetState(i, &tempXInputState);
dludwig@8471
   151
        if (result == ERROR_SUCCESS) {
dludwig@8471
   152
            g_XInputData[i].isDeviceConnected = SDL_TRUE;
dludwig@8471
   153
        }
dludwig@8471
   154
    }
dludwig@8471
   155
dludwig@8552
   156
    /* Start up the device-detection thread.
dludwig@8552
   157
     */
dludwig@8552
   158
    g_DeviceDetectionQuit = SDL_FALSE;
dludwig@8552
   159
    g_DeviceInfoLock = SDL_CreateMutex();
dludwig@8552
   160
    g_DeviceDetectionThread = SDL_CreateThread(DeviceDetectionThreadMain, "SDL_joystick", NULL);
dludwig@8552
   161
dludwig@8471
   162
    return (0);
dludwig@8471
   163
}
dludwig@8471
   164
dludwig@8471
   165
int SDL_SYS_NumJoysticks()
dludwig@8471
   166
{
dludwig@8471
   167
    int joystickCount = 0;
dludwig@8471
   168
    DWORD i;
dludwig@8471
   169
dludwig@8471
   170
    /* Iterate through each possible XInput device and see if something
dludwig@8471
   171
       was connected (at joystick init, or during the last polling).
dludwig@8471
   172
     */
dludwig@8552
   173
    SDL_LockMutex(g_DeviceInfoLock);
dludwig@8471
   174
    for (i = 0; i < XUSER_MAX_COUNT; ++i) {
dludwig@8471
   175
        if (g_XInputData[i].isDeviceConnected) {
dludwig@8471
   176
            ++joystickCount;
dludwig@8471
   177
        }
dludwig@8471
   178
    }
dludwig@8552
   179
    SDL_UnlockMutex(g_DeviceInfoLock);
dludwig@8471
   180
dludwig@8471
   181
    return joystickCount;
dludwig@8471
   182
}
dludwig@8471
   183
dludwig@8471
   184
void SDL_SYS_JoystickDetect()
dludwig@8471
   185
{
dludwig@8473
   186
    DWORD i;
dludwig@8473
   187
    SDL_Event event;
dludwig@8473
   188
dludwig@8473
   189
    /* Iterate through each possible XInput device, seeing if any devices
dludwig@8473
   190
       have been connected, or if they were removed.
dludwig@8473
   191
     */
dludwig@8552
   192
    SDL_LockMutex(g_DeviceInfoLock);
dludwig@8473
   193
    for (i = 0; i < XUSER_MAX_COUNT; ++i) {
dludwig@8473
   194
        /* See if any new devices are connected. */
dludwig@8552
   195
        if (g_XInputData[i].isDeviceConnectionEventPending) {
dludwig@8473
   196
#if !SDL_EVENTS_DISABLED
dludwig@8552
   197
            SDL_zero(event);
dludwig@8552
   198
            event.type = SDL_JOYDEVICEADDED;
dludwig@8552
   199
                
dludwig@8552
   200
            if (SDL_GetEventState(event.type) == SDL_ENABLE) {
dludwig@8552
   201
                event.jdevice.which = i;
dludwig@8552
   202
                if ((SDL_EventOK == NULL)
dludwig@8552
   203
                    || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
dludwig@8552
   204
                    SDL_PushEvent(&event);
dludwig@8473
   205
                }
dludwig@8552
   206
            }
dludwig@8473
   207
#endif
dludwig@8552
   208
            g_XInputData[i].isDeviceConnectionEventPending = SDL_FALSE;
dludwig@8473
   209
        } else if (g_XInputData[i].isDeviceRemovalEventPending) {
dludwig@8473
   210
            /* A device was previously marked as removed (by
dludwig@8473
   211
               SDL_SYS_JoystickUpdate).  Tell others about the device removal.
dludwig@8473
   212
            */
dludwig@8473
   213
dludwig@8473
   214
            g_XInputData[i].isDeviceRemovalEventPending = SDL_FALSE;
dludwig@8473
   215
dludwig@8473
   216
#if !SDL_EVENTS_DISABLED
dludwig@8552
   217
            SDL_zero(event);
dludwig@8552
   218
            event.type = SDL_JOYDEVICEREMOVED;
dludwig@8552
   219
                
dludwig@8552
   220
            if (SDL_GetEventState(event.type) == SDL_ENABLE) {
dludwig@8552
   221
                event.jdevice.which = i; //joystick->hwdata->userIndex;
dludwig@8552
   222
                if ((SDL_EventOK == NULL)
dludwig@8552
   223
                    || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
dludwig@8552
   224
                    SDL_PushEvent(&event);
dludwig@8552
   225
                }
dludwig@8473
   226
            }
dludwig@8552
   227
#endif
dludwig@8473
   228
        }
dludwig@8473
   229
    }
dludwig@8552
   230
    SDL_UnlockMutex(g_DeviceInfoLock);
dludwig@8471
   231
}
dludwig@8471
   232
dludwig@8471
   233
SDL_bool SDL_SYS_JoystickNeedsPolling()
dludwig@8471
   234
{
dludwig@8473
   235
    /* Since XInput, or WinRT, provides any events to indicate when a game
dludwig@8473
   236
       controller gets connected, and instead indicates device availability
dludwig@8473
   237
       solely through polling, we'll poll (for new devices).
dludwig@8473
   238
     */
dludwig@8473
   239
    return SDL_TRUE;
dludwig@8471
   240
}
dludwig@8471
   241
dludwig@8471
   242
/* Internal function to retreive device capabilities.
dludwig@8471
   243
   This function will return an SDL-standard value of 0 on success
dludwig@8471
   244
   (a device is connected, and data on it was retrieved), or -1
dludwig@8471
   245
   on failure (no device was connected, or some other error
dludwig@8471
   246
   occurred.  SDL_SetError() will be invoked to set an appropriate
dludwig@8471
   247
   error message.
dludwig@8471
   248
 */
dludwig@8471
   249
static int
dludwig@8471
   250
SDL_XInput_GetDeviceCapabilities(int device_index, XINPUT_CAPABILITIES * pDeviceCaps)
dludwig@8471
   251
{
dludwig@8471
   252
    HRESULT dwResult;
dludwig@8471
   253
dludwig@8471
   254
    /* Make sure that the device index is a valid one.  If not, return to the
dludwig@8471
   255
       caller with an error.
dludwig@8471
   256
     */
dludwig@8471
   257
    if (device_index < 0 || device_index >= XUSER_MAX_COUNT) {
dludwig@8471
   258
        return SDL_SetError("invalid/unavailable device index");
dludwig@8471
   259
    }
dludwig@8471
   260
dludwig@8471
   261
    /* See if a device exists, and if so, what its capabilities are.  If a
dludwig@8471
   262
       device is not available, return to the caller with an error.
dludwig@8471
   263
     */
dludwig@8471
   264
    switch ((dwResult = XInputGetCapabilities(device_index, 0, pDeviceCaps))) {
dludwig@8471
   265
        case ERROR_SUCCESS:
dludwig@8471
   266
            /* A device is available, and its capabilities were retrieved! */
dludwig@8471
   267
            return 0;
dludwig@8471
   268
        case ERROR_DEVICE_NOT_CONNECTED:
dludwig@8471
   269
            return SDL_SetError("no device is connected at joystick index, %d", device_index);
dludwig@8471
   270
        default:
dludwig@8471
   271
            return SDL_SetError("an unknown error occurred when retrieving info on a device at joystick index, %d", device_index);
dludwig@8471
   272
    }
dludwig@8471
   273
}
dludwig@8471
   274
dludwig@8471
   275
/* Function to get the device-dependent name of a joystick */
dludwig@8471
   276
const char *
dludwig@8471
   277
SDL_SYS_JoystickNameForDeviceIndex(int device_index)
dludwig@8471
   278
{
dludwig@8471
   279
    XINPUT_CAPABILITIES deviceCaps;
dludwig@8471
   280
dludwig@8471
   281
    if (SDL_XInput_GetDeviceCapabilities(device_index, &deviceCaps) != 0) {
dludwig@8471
   282
        /* Uh oh.  Device capabilities couldn't be retrieved.  Return to the
dludwig@8471
   283
           caller.  SDL_SetError() has already been invoked (with relevant
dludwig@8471
   284
           information).
dludwig@8471
   285
         */
dludwig@8471
   286
        return NULL;
dludwig@8471
   287
    }
dludwig@8471
   288
dludwig@8471
   289
    switch (deviceCaps.SubType) {
dludwig@8471
   290
        default:
dludwig@8471
   291
            if (deviceCaps.Type == XINPUT_DEVTYPE_GAMEPAD) {
dludwig@8471
   292
                return "Undefined game controller";
dludwig@8471
   293
            } else {
dludwig@8471
   294
                return "Undefined controller";
dludwig@8471
   295
            }
dludwig@8471
   296
        case XINPUT_DEVSUBTYPE_UNKNOWN:
dludwig@8471
   297
            if (deviceCaps.Type == XINPUT_DEVTYPE_GAMEPAD) {
dludwig@8471
   298
                return "Unknown game controller";
dludwig@8471
   299
            } else {
dludwig@8471
   300
                return "Unknown controller";
dludwig@8471
   301
            }
dludwig@8471
   302
        case XINPUT_DEVSUBTYPE_GAMEPAD:
dludwig@8471
   303
            return "Gamepad controller";
dludwig@8471
   304
        case XINPUT_DEVSUBTYPE_WHEEL:
dludwig@8471
   305
            return "Racing wheel controller";
dludwig@8471
   306
        case XINPUT_DEVSUBTYPE_ARCADE_STICK:
dludwig@8471
   307
            return "Arcade stick controller";
dludwig@8471
   308
        case XINPUT_DEVSUBTYPE_FLIGHT_STICK:
dludwig@8471
   309
            return "Flight stick controller";
dludwig@8471
   310
        case XINPUT_DEVSUBTYPE_DANCE_PAD:
dludwig@8471
   311
            return "Dance pad controller";
dludwig@8471
   312
        case XINPUT_DEVSUBTYPE_GUITAR:
dludwig@8471
   313
            return "Guitar controller";
dludwig@8471
   314
        case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE:
dludwig@8471
   315
            return "Guitar controller, Alternate";
dludwig@8471
   316
        case XINPUT_DEVSUBTYPE_GUITAR_BASS:
dludwig@8471
   317
            return "Guitar controller, Bass";
dludwig@8471
   318
        case XINPUT_DEVSUBTYPE_DRUM_KIT:
dludwig@8471
   319
            return "Drum controller";
dludwig@8471
   320
        case XINPUT_DEVSUBTYPE_ARCADE_PAD:
dludwig@8471
   321
            return "Arcade pad controller";
dludwig@8471
   322
    }
dludwig@8471
   323
}
dludwig@8471
   324
dludwig@8471
   325
/* Function to perform the mapping from device index to the instance id for this index */
dludwig@8471
   326
SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
dludwig@8471
   327
{
dludwig@8471
   328
    return device_index;
dludwig@8471
   329
}
dludwig@8471
   330
dludwig@8471
   331
/* Function to open a joystick for use.
dludwig@8471
   332
   The joystick to open is specified by the index field of the joystick.
dludwig@8471
   333
   This should fill the nbuttons and naxes fields of the joystick structure.
dludwig@8471
   334
   It returns 0, or -1 if there is an error.
dludwig@8471
   335
 */
dludwig@8471
   336
int
dludwig@8471
   337
SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
dludwig@8471
   338
{
dludwig@8471
   339
    XINPUT_CAPABILITIES deviceCaps;
dludwig@8471
   340
dludwig@8471
   341
    if (SDL_XInput_GetDeviceCapabilities(device_index, &deviceCaps) != 0) {
dludwig@8471
   342
        /* Uh oh.  Device capabilities couldn't be retrieved.  Return to the
dludwig@8471
   343
           caller.  SDL_SetError() has already been invoked (with relevant
dludwig@8471
   344
           information).
dludwig@8471
   345
         */
dludwig@8471
   346
        return -1;
dludwig@8471
   347
    }
dludwig@8471
   348
dludwig@8471
   349
    /* For now, only game pads are supported.  If the device is something other
dludwig@8471
   350
       than that, return an error to the caller.
dludwig@8471
   351
     */
dludwig@8471
   352
    if (deviceCaps.Type != XINPUT_DEVTYPE_GAMEPAD) {
dludwig@8471
   353
        return SDL_SetError("a device is connected (at joystick index, %d), but it is of an unknown device type (deviceCaps.Flags=%ul)",
dludwig@8471
   354
            device_index, (unsigned int)deviceCaps.Flags);
dludwig@8471
   355
    }
dludwig@8471
   356
dludwig@8552
   357
    /* Create the joystick data structure */
dludwig@8552
   358
    joystick->instance_id = device_index;
dludwig@8552
   359
    joystick->hwdata = &g_XInputData[device_index];
dludwig@8552
   360
dludwig@8552
   361
    // The XInput API has a hard coded button/axis mapping, so we just match it
dludwig@8552
   362
    joystick->naxes = 6;
dludwig@8552
   363
    joystick->nbuttons = 15;
dludwig@8552
   364
    joystick->nballs = 0;
dludwig@8552
   365
    joystick->nhats = 0;
dludwig@8552
   366
dludwig@8552
   367
    /* We're done! */
dludwig@8471
   368
    return (0);
dludwig@8471
   369
}
dludwig@8471
   370
dludwig@8471
   371
/* Function to determine is this joystick is attached to the system right now */
dludwig@8471
   372
SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
dludwig@8471
   373
{
dludwig@8552
   374
    SDL_bool isDeviceConnected;
dludwig@8552
   375
    SDL_LockMutex(g_DeviceInfoLock);
dludwig@8552
   376
    isDeviceConnected = joystick->hwdata->isDeviceConnected;
dludwig@8552
   377
    SDL_UnlockMutex(g_DeviceInfoLock);
dludwig@8552
   378
    return isDeviceConnected;
dludwig@8471
   379
}
dludwig@8471
   380
dludwig@8552
   381
/* Function to return > 0 if a bit array of buttons differs after applying a mask
dludwig@8552
   382
*/
dludwig@8552
   383
static int ButtonChanged( int ButtonsNow, int ButtonsPrev, int ButtonMask )
dludwig@8552
   384
{
dludwig@8552
   385
    return ( ButtonsNow & ButtonMask ) != ( ButtonsPrev & ButtonMask );
dludwig@8471
   386
}
dludwig@8471
   387
dludwig@8471
   388
/* Function to update the state of a joystick - called as a device poll.
dludwig@8471
   389
 * This function shouldn't update the joystick structure directly,
dludwig@8471
   390
 * but instead should call SDL_PrivateJoystick*() to deliver events
dludwig@8471
   391
 * and update joystick device state.
dludwig@8471
   392
 */
dludwig@8471
   393
void
dludwig@8471
   394
SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
dludwig@8471
   395
{
dludwig@8552
   396
    HRESULT result;
dludwig@8552
   397
    XINPUT_STATE prevXInputState;
dludwig@8552
   398
dludwig@8552
   399
    SDL_LockMutex(g_DeviceInfoLock);
dludwig@8552
   400
dludwig@8552
   401
    /* Before polling for new data, make note of the old data */
dludwig@8552
   402
    prevXInputState = joystick->hwdata->XInputState;
dludwig@8552
   403
dludwig@8552
   404
    /* Poll for new data */
dludwig@8552
   405
    result = XInputGetState(joystick->hwdata->userIndex, &joystick->hwdata->XInputState);
dludwig@8552
   406
    if (result == ERROR_DEVICE_NOT_CONNECTED) {
dludwig@8552
   407
        if (joystick->hwdata->isDeviceConnected) {
dludwig@8552
   408
            joystick->hwdata->isDeviceConnected = SDL_FALSE;
dludwig@8552
   409
            joystick->hwdata->isDeviceRemovalEventPending = SDL_TRUE;
dludwig@8552
   410
            /* TODO, WinRT: make sure isDeviceRemovalEventPending gets cleared as appropriate, and that quick re-plugs don't cause trouble */
dludwig@8552
   411
        }
dludwig@8552
   412
        SDL_UnlockMutex(g_DeviceInfoLock);
dludwig@8552
   413
        return;
dludwig@8471
   414
    }
dludwig@8552
   415
dludwig@8552
   416
    /* Make sure the device is marked as connected */
dludwig@8552
   417
    joystick->hwdata->isDeviceConnected = SDL_TRUE;
dludwig@8552
   418
dludwig@8552
   419
    // only fire events if the data changed from last time
dludwig@8552
   420
    if ( joystick->hwdata->XInputState.dwPacketNumber != 0 
dludwig@8552
   421
        && joystick->hwdata->XInputState.dwPacketNumber != prevXInputState.dwPacketNumber )
dludwig@8552
   422
    {
dludwig@8552
   423
        XINPUT_STATE *pXInputState = &joystick->hwdata->XInputState;
dludwig@8552
   424
        XINPUT_STATE *pXInputStatePrev = &prevXInputState;
dludwig@8552
   425
dludwig@8552
   426
        SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX );
dludwig@8552
   427
        SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-1*pXInputState->Gamepad.sThumbLY-1) );
dludwig@8552
   428
        SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX );
dludwig@8552
   429
        SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-1*pXInputState->Gamepad.sThumbRY-1) );
dludwig@8552
   430
        SDL_PrivateJoystickAxis(joystick, 4, (Sint16)((int)pXInputState->Gamepad.bLeftTrigger*32767/255) );
dludwig@8552
   431
        SDL_PrivateJoystickAxis(joystick, 5, (Sint16)((int)pXInputState->Gamepad.bRightTrigger*32767/255) );
dludwig@8552
   432
dludwig@8552
   433
        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_UP ) )
dludwig@8552
   434
            SDL_PrivateJoystickButton(joystick, 0, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ? SDL_PRESSED :	SDL_RELEASED );
dludwig@8552
   435
        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_DOWN ) )
dludwig@8552
   436
            SDL_PrivateJoystickButton(joystick, 1, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ? SDL_PRESSED :	SDL_RELEASED );
dludwig@8552
   437
        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_LEFT ) )
dludwig@8552
   438
            SDL_PrivateJoystickButton(joystick, 2, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ? SDL_PRESSED :	SDL_RELEASED );
dludwig@8552
   439
        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT ) )
dludwig@8552
   440
            SDL_PrivateJoystickButton(joystick, 3, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT ? SDL_PRESSED :	SDL_RELEASED );
dludwig@8552
   441
        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_START ) )
dludwig@8552
   442
            SDL_PrivateJoystickButton(joystick, 4, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_START ? SDL_PRESSED :	SDL_RELEASED );
dludwig@8552
   443
        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_BACK ) )
dludwig@8552
   444
            SDL_PrivateJoystickButton(joystick, 5, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_BACK ? SDL_PRESSED :	SDL_RELEASED );
dludwig@8552
   445
        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_THUMB ) )
dludwig@8552
   446
            SDL_PrivateJoystickButton(joystick, 6, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB ? SDL_PRESSED :	SDL_RELEASED );
dludwig@8552
   447
        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_THUMB ) )
dludwig@8552
   448
            SDL_PrivateJoystickButton(joystick, 7, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB ? SDL_PRESSED :	SDL_RELEASED );
dludwig@8552
   449
        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER ) )
dludwig@8552
   450
            SDL_PrivateJoystickButton(joystick, 8, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER ? SDL_PRESSED :	SDL_RELEASED );
dludwig@8552
   451
        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER ) )
dludwig@8552
   452
            SDL_PrivateJoystickButton(joystick, 9, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER ? SDL_PRESSED :	SDL_RELEASED );
dludwig@8552
   453
        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_A ) )
dludwig@8552
   454
            SDL_PrivateJoystickButton(joystick, 10, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_A ? SDL_PRESSED :	SDL_RELEASED );
dludwig@8552
   455
        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_B ) )
dludwig@8552
   456
            SDL_PrivateJoystickButton(joystick, 11, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_B ? SDL_PRESSED :	SDL_RELEASED );
dludwig@8552
   457
        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_X ) )
dludwig@8552
   458
            SDL_PrivateJoystickButton(joystick, 12, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_X ? SDL_PRESSED :	SDL_RELEASED );
dludwig@8552
   459
        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_Y ) )
dludwig@8552
   460
            SDL_PrivateJoystickButton(joystick, 13, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_Y ? SDL_PRESSED :	SDL_RELEASED );
dludwig@8552
   461
        if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons,  0x400 ) )
dludwig@8552
   462
            SDL_PrivateJoystickButton(joystick, 14, pXInputState->Gamepad.wButtons & 0x400 ? SDL_PRESSED :	SDL_RELEASED ); // 0x400 is the undocumented code for the guide button
dludwig@8552
   463
    }
dludwig@8552
   464
dludwig@8552
   465
    SDL_UnlockMutex(g_DeviceInfoLock);
dludwig@8471
   466
}
dludwig@8471
   467
dludwig@8471
   468
/* Function to close a joystick after use */
dludwig@8471
   469
void
dludwig@8471
   470
SDL_SYS_JoystickClose(SDL_Joystick * joystick)
dludwig@8471
   471
{
dludwig@8471
   472
    /* Clear cached button data on the joystick */
dludwig@8552
   473
    SDL_LockMutex(g_DeviceInfoLock);
dludwig@8471
   474
    SDL_zero(joystick->hwdata->XInputState);
dludwig@8552
   475
    SDL_UnlockMutex(g_DeviceInfoLock);
dludwig@8471
   476
dludwig@8471
   477
    /* There's need to free 'hwdata', as it's a pointer to a global array.
dludwig@8471
   478
       The field will be cleared anyways, just to indicate that it's not
dludwig@8471
   479
       currently needed.
dludwig@8471
   480
     */
dludwig@8471
   481
    joystick->hwdata = NULL;
dludwig@8471
   482
}
dludwig@8471
   483
dludwig@8471
   484
/* Function to perform any system-specific joystick related cleanup */
dludwig@8471
   485
void
dludwig@8471
   486
SDL_SYS_JoystickQuit(void)
dludwig@8471
   487
{
dludwig@8552
   488
    /* Tell the joystick detection thread to stop, then wait for it to finish */
dludwig@8552
   489
    SDL_LockMutex(g_DeviceInfoLock);
dludwig@8552
   490
    g_DeviceDetectionQuit = SDL_TRUE;
dludwig@8552
   491
    SDL_UnlockMutex(g_DeviceInfoLock);
dludwig@8552
   492
    SDL_WaitThread(g_DeviceDetectionThread, NULL);
dludwig@8552
   493
dludwig@8552
   494
    /* Clean up device-detection stuff */
dludwig@8552
   495
    SDL_DestroyMutex(g_DeviceInfoLock);
dludwig@8552
   496
    g_DeviceInfoLock = NULL;
dludwig@8552
   497
    g_DeviceDetectionThread = NULL;
dludwig@8552
   498
    g_DeviceDetectionQuit = SDL_FALSE;
dludwig@8552
   499
dludwig@8471
   500
    return;
dludwig@8471
   501
}
dludwig@8471
   502
dludwig@8471
   503
SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
dludwig@8471
   504
{
dludwig@8471
   505
    SDL_JoystickGUID guid;
dludwig@8471
   506
    // the GUID is just the first 16 chars of the name for now
dludwig@8471
   507
    const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index );
dludwig@8471
   508
    SDL_zero( guid );
dludwig@8471
   509
    SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
dludwig@8471
   510
    return guid;
dludwig@8471
   511
}
dludwig@8471
   512
dludwig@8471
   513
dludwig@8471
   514
SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
dludwig@8471
   515
{
dludwig@8471
   516
    SDL_JoystickGUID guid;
dludwig@8471
   517
    // the GUID is just the first 16 chars of the name for now
dludwig@8471
   518
    const char *name = joystick->name;
dludwig@8471
   519
    SDL_zero( guid );
dludwig@8471
   520
    SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
dludwig@8471
   521
    return guid;
dludwig@8471
   522
}
dludwig@8471
   523
dludwig@8552
   524
SDL_bool SDL_SYS_IsXInputDeviceIndex(int device_index)
dludwig@8552
   525
{
dludwig@8552
   526
    /* The XInput-capable DirectInput joystick backend implements the same
dludwig@8552
   527
       function (SDL_SYS_IsXInputDeviceIndex), however in that case, not all
dludwig@8552
   528
       joystick devices are XInput devices.  In this case, with the
dludwig@8552
   529
       WinRT-enabled XInput-only backend, all "joystick" devices are XInput
dludwig@8552
   530
       devices.
dludwig@8552
   531
     */
dludwig@8552
   532
    return SDL_TRUE;
dludwig@8471
   533
}
dludwig@8471
   534
dludwig@8471
   535
#endif /* SDL_JOYSTICK_XINPUT */
dludwig@8471
   536
dludwig@8471
   537
/* vi: set ts=4 sw=4 expandtab: */