SDL_xinputjoystick.c (12265B)
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 */ 35static SDL_bool s_bXInputEnabled = SDL_TRUE; 36 37 38static SDL_bool 39SDL_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 49SDL_bool SDL_XINPUT_Enabled(void) 50{ 51 return s_bXInputEnabled; 52} 53 54int 55SDL_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 68static char * 69GetXInputName(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 111static void 112AddXInputDevice(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 170void 171SDL_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 189int 190SDL_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 < XUSER_MAX_COUNT); 200 201 joystick->hwdata->bXInputDevice = SDL_TRUE; 202 203 if (XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities) != ERROR_SUCCESS) { 204 SDL_free(joystick->hwdata); 205 joystick->hwdata = NULL; 206 return SDL_SetError("Failed to obtain XInput device capabilities. Device disconnected?"); 207 } 208 SDL_zero(state); 209 joystick->hwdata->bXInputHaptic = (XINPUTSETSTATE(userId, &state) == ERROR_SUCCESS); 210 joystick->hwdata->userid = userId; 211 212 /* The XInput API has a hard coded button/axis mapping, so we just match it */ 213 if (SDL_XInputUseOldJoystickMapping()) { 214 joystick->naxes = 6; 215 joystick->nbuttons = 15; 216 } else { 217 joystick->naxes = 6; 218 joystick->nbuttons = 11; 219 joystick->nhats = 1; 220 } 221 return 0; 222} 223 224static void 225UpdateXInputJoystickState_OLD(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState) 226{ 227 static WORD s_XInputButtons[] = { 228 XINPUT_GAMEPAD_DPAD_UP, XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_RIGHT, 229 XINPUT_GAMEPAD_START, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB, 230 XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, 231 XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y, 232 XINPUT_GAMEPAD_GUIDE 233 }; 234 WORD wButtons = pXInputState->Gamepad.wButtons; 235 Uint8 button; 236 237 SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX); 238 SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY))); 239 SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX); 240 SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY))); 241 SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768)); 242 SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768)); 243 244 for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) { 245 SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED); 246 } 247} 248 249static void 250UpdateXInputJoystickState(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState) 251{ 252 static WORD s_XInputButtons[] = { 253 XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y, 254 XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_START, 255 XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB, 256 XINPUT_GAMEPAD_GUIDE 257 }; 258 WORD wButtons = pXInputState->Gamepad.wButtons; 259 Uint8 button; 260 Uint8 hat = SDL_HAT_CENTERED; 261 262 SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX); 263 SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY))); 264 SDL_PrivateJoystickAxis(joystick, 2, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768)); 265 SDL_PrivateJoystickAxis(joystick, 3, (Sint16)pXInputState->Gamepad.sThumbRX); 266 SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY))); 267 SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768)); 268 269 for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) { 270 SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED); 271 } 272 273 if (wButtons & XINPUT_GAMEPAD_DPAD_UP) { 274 hat |= SDL_HAT_UP; 275 } 276 if (wButtons & XINPUT_GAMEPAD_DPAD_DOWN) { 277 hat |= SDL_HAT_DOWN; 278 } 279 if (wButtons & XINPUT_GAMEPAD_DPAD_LEFT) { 280 hat |= SDL_HAT_LEFT; 281 } 282 if (wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) { 283 hat |= SDL_HAT_RIGHT; 284 } 285 SDL_PrivateJoystickHat(joystick, 0, hat); 286} 287 288void 289SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick) 290{ 291 HRESULT result; 292 XINPUT_STATE_EX XInputState; 293 294 if (!XINPUTGETSTATE) 295 return; 296 297 result = XINPUTGETSTATE(joystick->hwdata->userid, &XInputState); 298 if (result == ERROR_DEVICE_NOT_CONNECTED) { 299 joystick->hwdata->send_remove_event = SDL_TRUE; 300 joystick->hwdata->removed = SDL_TRUE; 301 return; 302 } 303 304 /* only fire events if the data changed from last time */ 305 if (XInputState.dwPacketNumber && XInputState.dwPacketNumber != joystick->hwdata->dwPacketNumber) { 306 if (SDL_XInputUseOldJoystickMapping()) { 307 UpdateXInputJoystickState_OLD(joystick, &XInputState); 308 } else { 309 UpdateXInputJoystickState(joystick, &XInputState); 310 } 311 joystick->hwdata->dwPacketNumber = XInputState.dwPacketNumber; 312 } 313} 314 315void 316SDL_XINPUT_JoystickClose(SDL_Joystick * joystick) 317{ 318} 319 320void 321SDL_XINPUT_JoystickQuit(void) 322{ 323 if (s_bXInputEnabled) { 324 WIN_UnloadXInputDLL(); 325 } 326} 327 328SDL_bool 329SDL_SYS_IsXInputGamepad_DeviceIndex(int device_index) 330{ 331 JoyStick_DeviceData *device = SYS_Joystick; 332 int index; 333 334 for (index = device_index; index > 0; index--) 335 device = device->pNext; 336 337 return (device->SubType == XINPUT_DEVSUBTYPE_GAMEPAD); 338} 339 340#else /* !SDL_JOYSTICK_XINPUT */ 341 342 343SDL_bool SDL_XINPUT_Enabled(void) 344{ 345 return SDL_FALSE; 346} 347 348int 349SDL_XINPUT_JoystickInit(void) 350{ 351 return 0; 352} 353 354void 355SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext) 356{ 357} 358 359int 360SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice) 361{ 362 return SDL_Unsupported(); 363} 364 365void 366SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick) 367{ 368} 369 370void 371SDL_XINPUT_JoystickClose(SDL_Joystick * joystick) 372{ 373} 374 375void 376SDL_XINPUT_JoystickQuit(void) 377{ 378} 379 380#endif /* SDL_JOYSTICK_XINPUT */ 381 382/* vi: set ts=4 sw=4 expandtab: */