src/joystick/windows/SDL_xinputjoystick.c
changeset 8972 dfc759d7486f
child 8976 1a5d959d7b32
equal deleted inserted replaced
8971:c30e826412d1 8972:dfc759d7486f
       
     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     Uint8 hat = SDL_HAT_CENTERED;
       
   238 
       
   239     SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX);
       
   240     SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY)));
       
   241     SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX);
       
   242     SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY)));
       
   243     SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768));
       
   244     SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768));
       
   245 
       
   246     for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) {
       
   247         SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED);
       
   248     }
       
   249 }
       
   250 
       
   251 static void
       
   252 UpdateXInputJoystickState(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState)
       
   253 {
       
   254     static WORD s_XInputButtons[] = {
       
   255         XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y,
       
   256         XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_START,
       
   257         XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB,
       
   258         XINPUT_GAMEPAD_GUIDE
       
   259     };
       
   260     WORD wButtons = pXInputState->Gamepad.wButtons;
       
   261     Uint8 button;
       
   262     Uint8 hat = SDL_HAT_CENTERED;
       
   263 
       
   264     SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX);
       
   265     SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY)));
       
   266     SDL_PrivateJoystickAxis(joystick, 2, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768));
       
   267     SDL_PrivateJoystickAxis(joystick, 3, (Sint16)pXInputState->Gamepad.sThumbRX);
       
   268     SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY)));
       
   269     SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768));
       
   270 
       
   271     for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) {
       
   272         SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED);
       
   273     }
       
   274 
       
   275     if (wButtons & XINPUT_GAMEPAD_DPAD_UP) {
       
   276         hat |= SDL_HAT_UP;
       
   277     }
       
   278     if (wButtons & XINPUT_GAMEPAD_DPAD_DOWN) {
       
   279         hat |= SDL_HAT_DOWN;
       
   280     }
       
   281     if (wButtons & XINPUT_GAMEPAD_DPAD_LEFT) {
       
   282         hat |= SDL_HAT_LEFT;
       
   283     }
       
   284     if (wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) {
       
   285         hat |= SDL_HAT_RIGHT;
       
   286     }
       
   287     SDL_PrivateJoystickHat(joystick, 0, hat);
       
   288 }
       
   289 
       
   290 void
       
   291 SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
       
   292 {
       
   293     HRESULT result;
       
   294     XINPUT_STATE_EX XInputState;
       
   295 
       
   296     if (!XINPUTGETSTATE)
       
   297         return;
       
   298 
       
   299     result = XINPUTGETSTATE(joystick->hwdata->userid, &XInputState);
       
   300     if (result == ERROR_DEVICE_NOT_CONNECTED) {
       
   301         joystick->hwdata->send_remove_event = SDL_TRUE;
       
   302         joystick->hwdata->removed = SDL_TRUE;
       
   303         return;
       
   304     }
       
   305 
       
   306     /* only fire events if the data changed from last time */
       
   307     if (XInputState.dwPacketNumber && XInputState.dwPacketNumber != joystick->hwdata->dwPacketNumber) {
       
   308         if (SDL_XInputUseOldJoystickMapping()) {
       
   309             UpdateXInputJoystickState_OLD(joystick, &XInputState);
       
   310         } else {
       
   311             UpdateXInputJoystickState(joystick, &XInputState);
       
   312         }
       
   313         joystick->hwdata->dwPacketNumber = XInputState.dwPacketNumber;
       
   314     }
       
   315 }
       
   316 
       
   317 void
       
   318 SDL_XINPUT_JoystickClose(SDL_Joystick * joystick)
       
   319 {
       
   320 }
       
   321 
       
   322 void
       
   323 SDL_XINPUT_JoystickQuit(void)
       
   324 {
       
   325     if (s_bXInputEnabled) {
       
   326         WIN_UnloadXInputDLL();
       
   327     }
       
   328 }
       
   329 
       
   330 SDL_bool
       
   331 SDL_SYS_IsXInputGamepad_DeviceIndex(int device_index)
       
   332 {
       
   333     JoyStick_DeviceData *device = SYS_Joystick;
       
   334     int index;
       
   335 
       
   336     for (index = device_index; index > 0; index--)
       
   337         device = device->pNext;
       
   338 
       
   339     return (device->SubType == XINPUT_DEVSUBTYPE_GAMEPAD);
       
   340 }
       
   341 
       
   342 #else /* !SDL_JOYSTICK_XINPUT */
       
   343 
       
   344 
       
   345 SDL_bool SDL_XINPUT_Enabled(void)
       
   346 {
       
   347     return SDL_FALSE;
       
   348 }
       
   349 
       
   350 int
       
   351 SDL_XINPUT_JoystickInit(void)
       
   352 {
       
   353     return 0;
       
   354 }
       
   355 
       
   356 void
       
   357 SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
       
   358 {
       
   359 }
       
   360 
       
   361 int
       
   362 SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
       
   363 {
       
   364     return SDL_Unsupported();
       
   365 }
       
   366 
       
   367 void
       
   368 SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
       
   369 {
       
   370 }
       
   371 
       
   372 void
       
   373 SDL_XINPUT_JoystickClose(SDL_Joystick * joystick)
       
   374 {
       
   375 }
       
   376 
       
   377 void
       
   378 SDL_XINPUT_JoystickQuit(void)
       
   379 {
       
   380 }
       
   381 
       
   382 SDL_bool
       
   383 SDL_SYS_IsXInputGamepad_DeviceIndex(int device_index)
       
   384 {
       
   385     return SDL_FALSE;
       
   386 }
       
   387 
       
   388 #endif /* SDL_JOYSTICK_XINPUT */
       
   389 
       
   390 /* vi: set ts=4 sw=4 expandtab: */