src/joystick/windows/SDL_xinputjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 07 Jul 2014 10:26:28 -0700
changeset 8976 1a5d959d7b32
parent 8972 dfc759d7486f
child 8987 d0fd8420b019
permissions -rw-r--r--
Fixed mingw64 build and warnings
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #include "SDL_assert.h"
    24 #include "SDL_hints.h"
    25 #include "../SDL_sysjoystick.h"
    26 #include "SDL_windowsjoystick_c.h"
    27 #include "SDL_xinputjoystick_c.h"
    28 
    29 
    30 #if SDL_JOYSTICK_XINPUT
    31 
    32 /*
    33  * Internal stuff.
    34  */
    35 static SDL_bool s_bXInputEnabled = SDL_TRUE;
    36 
    37 
    38 static SDL_bool
    39 SDL_XInputUseOldJoystickMapping()
    40 {
    41     static int s_XInputUseOldJoystickMapping = -1;
    42     if (s_XInputUseOldJoystickMapping < 0) {
    43         const char *hint = SDL_GetHint(SDL_HINT_XINPUT_USE_OLD_JOYSTICK_MAPPING);
    44         s_XInputUseOldJoystickMapping = (hint && *hint == '1') ? 1 : 0;
    45     }
    46     return (s_XInputUseOldJoystickMapping > 0);
    47 }
    48 
    49 SDL_bool SDL_XINPUT_Enabled(void)
    50 {
    51     return s_bXInputEnabled;
    52 }
    53 
    54 int
    55 SDL_XINPUT_JoystickInit(void)
    56 {
    57     const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED);
    58     if (env && !SDL_atoi(env)) {
    59         s_bXInputEnabled = SDL_FALSE;
    60     }
    61 
    62     if (s_bXInputEnabled && WIN_LoadXInputDLL() < 0) {
    63         s_bXInputEnabled = SDL_FALSE;  /* oh well. */
    64     }
    65     return 0;
    66 }
    67 
    68 static char *
    69 GetXInputName(const Uint8 userid, BYTE SubType)
    70 {
    71     char name[32];
    72 
    73     if (SDL_XInputUseOldJoystickMapping()) {
    74         SDL_snprintf(name, sizeof(name), "X360 Controller #%u", 1 + userid);
    75     } else {
    76         switch (SubType) {
    77         case XINPUT_DEVSUBTYPE_GAMEPAD:
    78             SDL_snprintf(name, sizeof(name), "XInput Controller #%u", 1 + userid);
    79             break;
    80         case XINPUT_DEVSUBTYPE_WHEEL:
    81             SDL_snprintf(name, sizeof(name), "XInput Wheel #%u", 1 + userid);
    82             break;
    83         case XINPUT_DEVSUBTYPE_ARCADE_STICK:
    84             SDL_snprintf(name, sizeof(name), "XInput ArcadeStick #%u", 1 + userid);
    85             break;
    86         case XINPUT_DEVSUBTYPE_FLIGHT_STICK:
    87             SDL_snprintf(name, sizeof(name), "XInput FlightStick #%u", 1 + userid);
    88             break;
    89         case XINPUT_DEVSUBTYPE_DANCE_PAD:
    90             SDL_snprintf(name, sizeof(name), "XInput DancePad #%u", 1 + userid);
    91             break;
    92         case XINPUT_DEVSUBTYPE_GUITAR:
    93         case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE:
    94         case XINPUT_DEVSUBTYPE_GUITAR_BASS:
    95             SDL_snprintf(name, sizeof(name), "XInput Guitar #%u", 1 + userid);
    96             break;
    97         case XINPUT_DEVSUBTYPE_DRUM_KIT:
    98             SDL_snprintf(name, sizeof(name), "XInput DrumKit #%u", 1 + userid);
    99             break;
   100         case XINPUT_DEVSUBTYPE_ARCADE_PAD:
   101             SDL_snprintf(name, sizeof(name), "XInput ArcadePad #%u", 1 + userid);
   102             break;
   103         default:
   104             SDL_snprintf(name, sizeof(name), "XInput Device #%u", 1 + userid);
   105             break;
   106         }
   107     }
   108     return SDL_strdup(name);
   109 }
   110 
   111 static void
   112 AddXInputDevice(const Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext)
   113 {
   114     JoyStick_DeviceData *pPrevJoystick = NULL;
   115     JoyStick_DeviceData *pNewJoystick = *pContext;
   116 
   117     if (SDL_XInputUseOldJoystickMapping() && SubType != XINPUT_DEVSUBTYPE_GAMEPAD)
   118         return;
   119 
   120     if (SubType == XINPUT_DEVSUBTYPE_UNKNOWN)
   121         return;
   122 
   123     while (pNewJoystick) {
   124         if (pNewJoystick->bXInputDevice && (pNewJoystick->XInputUserId == userid) && (pNewJoystick->SubType == SubType)) {
   125             /* if we are replacing the front of the list then update it */
   126             if (pNewJoystick == *pContext) {
   127                 *pContext = pNewJoystick->pNext;
   128             } else if (pPrevJoystick) {
   129                 pPrevJoystick->pNext = pNewJoystick->pNext;
   130             }
   131 
   132             pNewJoystick->pNext = SYS_Joystick;
   133             SYS_Joystick = pNewJoystick;
   134             return;   /* already in the list. */
   135         }
   136 
   137         pPrevJoystick = pNewJoystick;
   138         pNewJoystick = pNewJoystick->pNext;
   139     }
   140 
   141     pNewJoystick = (JoyStick_DeviceData *)SDL_malloc(sizeof(JoyStick_DeviceData));
   142     if (!pNewJoystick) {
   143         return; /* better luck next time? */
   144     }
   145     SDL_zerop(pNewJoystick);
   146 
   147     pNewJoystick->joystickname = GetXInputName(userid, SubType);
   148     if (!pNewJoystick->joystickname) {
   149         SDL_free(pNewJoystick);
   150         return; /* better luck next time? */
   151     }
   152 
   153     pNewJoystick->bXInputDevice = SDL_TRUE;
   154     if (SDL_XInputUseOldJoystickMapping()) {
   155         SDL_zero(pNewJoystick->guid);
   156     } else {
   157         pNewJoystick->guid.data[0] = 'x';
   158         pNewJoystick->guid.data[1] = 'i';
   159         pNewJoystick->guid.data[2] = 'n';
   160         pNewJoystick->guid.data[3] = 'p';
   161         pNewJoystick->guid.data[4] = 'u';
   162         pNewJoystick->guid.data[5] = 't';
   163         pNewJoystick->guid.data[6] = SubType;
   164     }
   165     pNewJoystick->SubType = SubType;
   166     pNewJoystick->XInputUserId = userid;
   167     SDL_SYS_AddJoystickDevice(pNewJoystick);
   168 }
   169 
   170 void
   171 SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
   172 {
   173     int iuserid;
   174 
   175     if (!s_bXInputEnabled) {
   176         return;
   177     }
   178 
   179     /* iterate in reverse, so these are in the final list in ascending numeric order. */
   180     for (iuserid = XUSER_MAX_COUNT - 1; iuserid >= 0; iuserid--) {
   181         const Uint8 userid = (Uint8)iuserid;
   182         XINPUT_CAPABILITIES capabilities;
   183         if (XINPUTGETCAPABILITIES(userid, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS) {
   184             AddXInputDevice(userid, capabilities.SubType, pContext);
   185         }
   186     }
   187 }
   188 
   189 int
   190 SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
   191 {
   192     const Uint8 userId = joystickdevice->XInputUserId;
   193     XINPUT_CAPABILITIES capabilities;
   194     XINPUT_VIBRATION state;
   195 
   196     SDL_assert(s_bXInputEnabled);
   197     SDL_assert(XINPUTGETCAPABILITIES);
   198     SDL_assert(XINPUTSETSTATE);
   199     SDL_assert(userId >= 0);
   200     SDL_assert(userId < XUSER_MAX_COUNT);
   201 
   202     joystick->hwdata->bXInputDevice = SDL_TRUE;
   203 
   204     if (XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities) != ERROR_SUCCESS) {
   205         SDL_free(joystick->hwdata);
   206         joystick->hwdata = NULL;
   207         return SDL_SetError("Failed to obtain XInput device capabilities. Device disconnected?");
   208     }
   209     SDL_zero(state);
   210     joystick->hwdata->bXInputHaptic = (XINPUTSETSTATE(userId, &state) == ERROR_SUCCESS);
   211     joystick->hwdata->userid = userId;
   212 
   213     /* The XInput API has a hard coded button/axis mapping, so we just match it */
   214     if (SDL_XInputUseOldJoystickMapping()) {
   215         joystick->naxes = 6;
   216         joystick->nbuttons = 15;
   217     } else {
   218         joystick->naxes = 6;
   219         joystick->nbuttons = 11;
   220         joystick->nhats = 1;
   221     }
   222     return 0;
   223 }
   224 
   225 static void
   226 UpdateXInputJoystickState_OLD(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState)
   227 {
   228     static WORD s_XInputButtons[] = {
   229         XINPUT_GAMEPAD_DPAD_UP, XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_RIGHT,
   230         XINPUT_GAMEPAD_START, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB,
   231         XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER,
   232         XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y,
   233         XINPUT_GAMEPAD_GUIDE
   234     };
   235     WORD wButtons = pXInputState->Gamepad.wButtons;
   236     Uint8 button;
   237 
   238     SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX);
   239     SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY)));
   240     SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX);
   241     SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY)));
   242     SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768));
   243     SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768));
   244 
   245     for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) {
   246         SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED);
   247     }
   248 }
   249 
   250 static void
   251 UpdateXInputJoystickState(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState)
   252 {
   253     static WORD s_XInputButtons[] = {
   254         XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y,
   255         XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_START,
   256         XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB,
   257         XINPUT_GAMEPAD_GUIDE
   258     };
   259     WORD wButtons = pXInputState->Gamepad.wButtons;
   260     Uint8 button;
   261     Uint8 hat = SDL_HAT_CENTERED;
   262 
   263     SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX);
   264     SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY)));
   265     SDL_PrivateJoystickAxis(joystick, 2, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768));
   266     SDL_PrivateJoystickAxis(joystick, 3, (Sint16)pXInputState->Gamepad.sThumbRX);
   267     SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY)));
   268     SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768));
   269 
   270     for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) {
   271         SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED);
   272     }
   273 
   274     if (wButtons & XINPUT_GAMEPAD_DPAD_UP) {
   275         hat |= SDL_HAT_UP;
   276     }
   277     if (wButtons & XINPUT_GAMEPAD_DPAD_DOWN) {
   278         hat |= SDL_HAT_DOWN;
   279     }
   280     if (wButtons & XINPUT_GAMEPAD_DPAD_LEFT) {
   281         hat |= SDL_HAT_LEFT;
   282     }
   283     if (wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) {
   284         hat |= SDL_HAT_RIGHT;
   285     }
   286     SDL_PrivateJoystickHat(joystick, 0, hat);
   287 }
   288 
   289 void
   290 SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
   291 {
   292     HRESULT result;
   293     XINPUT_STATE_EX XInputState;
   294 
   295     if (!XINPUTGETSTATE)
   296         return;
   297 
   298     result = XINPUTGETSTATE(joystick->hwdata->userid, &XInputState);
   299     if (result == ERROR_DEVICE_NOT_CONNECTED) {
   300         joystick->hwdata->send_remove_event = SDL_TRUE;
   301         joystick->hwdata->removed = SDL_TRUE;
   302         return;
   303     }
   304 
   305     /* only fire events if the data changed from last time */
   306     if (XInputState.dwPacketNumber && XInputState.dwPacketNumber != joystick->hwdata->dwPacketNumber) {
   307         if (SDL_XInputUseOldJoystickMapping()) {
   308             UpdateXInputJoystickState_OLD(joystick, &XInputState);
   309         } else {
   310             UpdateXInputJoystickState(joystick, &XInputState);
   311         }
   312         joystick->hwdata->dwPacketNumber = XInputState.dwPacketNumber;
   313     }
   314 }
   315 
   316 void
   317 SDL_XINPUT_JoystickClose(SDL_Joystick * joystick)
   318 {
   319 }
   320 
   321 void
   322 SDL_XINPUT_JoystickQuit(void)
   323 {
   324     if (s_bXInputEnabled) {
   325         WIN_UnloadXInputDLL();
   326     }
   327 }
   328 
   329 SDL_bool
   330 SDL_SYS_IsXInputGamepad_DeviceIndex(int device_index)
   331 {
   332     JoyStick_DeviceData *device = SYS_Joystick;
   333     int index;
   334 
   335     for (index = device_index; index > 0; index--)
   336         device = device->pNext;
   337 
   338     return (device->SubType == XINPUT_DEVSUBTYPE_GAMEPAD);
   339 }
   340 
   341 #else /* !SDL_JOYSTICK_XINPUT */
   342 
   343 
   344 SDL_bool SDL_XINPUT_Enabled(void)
   345 {
   346     return SDL_FALSE;
   347 }
   348 
   349 int
   350 SDL_XINPUT_JoystickInit(void)
   351 {
   352     return 0;
   353 }
   354 
   355 void
   356 SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
   357 {
   358 }
   359 
   360 int
   361 SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
   362 {
   363     return SDL_Unsupported();
   364 }
   365 
   366 void
   367 SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
   368 {
   369 }
   370 
   371 void
   372 SDL_XINPUT_JoystickClose(SDL_Joystick * joystick)
   373 {
   374 }
   375 
   376 void
   377 SDL_XINPUT_JoystickQuit(void)
   378 {
   379 }
   380 
   381 #endif /* SDL_JOYSTICK_XINPUT */
   382 
   383 /* vi: set ts=4 sw=4 expandtab: */