SDL_rawinputjoystick.c (71804B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 2019 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 */ 22 /* 23 RAWINPUT Joystick API for better handling XInput-capable devices on Windows. 24 25 XInput is limited to 4 devices. 26 Windows.Gaming.Input does not get inputs from XBox One controllers when not in the foreground. 27 DirectInput does not get inputs from XBox One controllers when not in the foreground, nor rumble or accurate triggers. 28 RawInput does not get rumble or accurate triggers. 29 30 So, combine them as best we can! 31 */ 32 #include "../../SDL_internal.h" 33 34 #if SDL_JOYSTICK_RAWINPUT 35 36 #include "SDL_endian.h" 37 #include "SDL_events.h" 38 #include "SDL_hints.h" 39 #include "SDL_mutex.h" 40 #include "SDL_timer.h" 41 #include "../usb_ids.h" 42 #include "../SDL_sysjoystick.h" 43 #include "../../core/windows/SDL_windows.h" 44 #include "../../core/windows/SDL_hid.h" 45 #include "../hidapi/SDL_hidapijoystick_c.h" 46 47 #define SDL_JOYSTICK_RAWINPUT_XINPUT 48 #ifdef SDL_WINDOWS10_SDK 49 #define SDL_JOYSTICK_RAWINPUT_WGI 50 #endif 51 52 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT 53 #include "../../core/windows/SDL_xinput.h" 54 #endif 55 56 #ifdef SDL_JOYSTICK_RAWINPUT_WGI 57 #include "../../core/windows/SDL_windows.h" 58 typedef struct WindowsGamingInputGamepadState WindowsGamingInputGamepadState; 59 #define GamepadButtons_GUIDE 0x40000000 60 #define COBJMACROS 61 #include "windows.gaming.input.h" 62 #endif 63 64 #if defined(SDL_JOYSTICK_RAWINPUT_XINPUT) || defined(SDL_JOYSTICK_RAWINPUT_WGI) 65 #define SDL_JOYSTICK_RAWINPUT_MATCHING 66 #define SDL_JOYSTICK_RAWINPUT_MATCH_AXES 67 #endif 68 69 /*#define DEBUG_RAWINPUT*/ 70 71 #ifndef RIDEV_EXINPUTSINK 72 #define RIDEV_EXINPUTSINK 0x00001000 73 #define RIDEV_DEVNOTIFY 0x00002000 74 #endif 75 76 #ifndef WM_INPUT_DEVICE_CHANGE 77 #define WM_INPUT_DEVICE_CHANGE 0x00FE 78 #endif 79 #ifndef WM_INPUT 80 #define WM_INPUT 0x00FF 81 #endif 82 #ifndef GIDC_ARRIVAL 83 #define GIDC_ARRIVAL 1 84 #define GIDC_REMOVAL 2 85 #endif 86 87 88 static SDL_bool SDL_RAWINPUT_inited = SDL_FALSE; 89 static int SDL_RAWINPUT_numjoysticks = 0; 90 static SDL_mutex *SDL_RAWINPUT_mutex = NULL; 91 92 static void RAWINPUT_JoystickClose(SDL_Joystick *joystick); 93 94 typedef struct _SDL_RAWINPUT_Device 95 { 96 SDL_atomic_t refcount; 97 char *name; 98 Uint16 vendor_id; 99 Uint16 product_id; 100 Uint16 version; 101 SDL_JoystickGUID guid; 102 SDL_bool is_xinput; 103 PHIDP_PREPARSED_DATA preparsed_data; 104 105 HANDLE hDevice; 106 SDL_Joystick *joystick; 107 SDL_JoystickID joystick_id; 108 109 struct _SDL_RAWINPUT_Device *next; 110 } SDL_RAWINPUT_Device; 111 112 struct joystick_hwdata 113 { 114 SDL_bool is_xinput; 115 PHIDP_PREPARSED_DATA preparsed_data; 116 ULONG max_data_length; 117 HIDP_DATA *data; 118 USHORT *button_indices; 119 USHORT *axis_indices; 120 USHORT *hat_indices; 121 SDL_bool guide_hack; 122 SDL_bool trigger_hack; 123 USHORT trigger_hack_index; 124 125 #ifdef SDL_JOYSTICK_RAWINPUT_MATCHING 126 Uint32 match_state; /* Low 16 bits for button states, high 16 for 4 4bit axes */ 127 Uint32 last_state_packet; 128 #endif 129 130 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT 131 SDL_bool xinput_enabled; 132 SDL_bool xinput_correlated; 133 Uint8 xinput_correlation_id; 134 Uint8 xinput_correlation_count; 135 Uint8 xinput_uncorrelate_count; 136 Uint8 xinput_slot; 137 #endif 138 139 #ifdef SDL_JOYSTICK_RAWINPUT_WGI 140 SDL_bool wgi_correlated; 141 Uint8 wgi_correlation_id; 142 Uint8 wgi_correlation_count; 143 Uint8 wgi_uncorrelate_count; 144 WindowsGamingInputGamepadState *wgi_slot; 145 #endif 146 147 SDL_RAWINPUT_Device *device; 148 }; 149 typedef struct joystick_hwdata RAWINPUT_DeviceContext; 150 151 SDL_RAWINPUT_Device *SDL_RAWINPUT_devices; 152 153 static const Uint16 subscribed_devices[] = { 154 USB_USAGE_GENERIC_GAMEPAD, 155 /* Don't need Joystick for any devices we're handling here (XInput-capable) 156 USB_USAGE_GENERIC_JOYSTICK, 157 USB_USAGE_GENERIC_MULTIAXISCONTROLLER, 158 */ 159 }; 160 161 #ifdef SDL_JOYSTICK_RAWINPUT_MATCHING 162 163 static struct { 164 Uint32 last_state_packet; 165 SDL_Joystick *joystick; 166 SDL_Joystick *last_joystick; 167 } guide_button_candidate; 168 169 typedef struct WindowsMatchState { 170 #ifdef SDL_JOYSTICK_RAWINPUT_MATCH_AXES 171 SHORT match_axes[4]; 172 #endif 173 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT 174 WORD xinput_buttons; 175 #endif 176 #ifdef SDL_JOYSTICK_RAWINPUT_WGI 177 Uint32 wgi_buttons; 178 #endif 179 SDL_bool any_data; 180 } WindowsMatchState; 181 182 static void RAWINPUT_FillMatchState(WindowsMatchState *state, Uint32 match_state) 183 { 184 #ifdef SDL_JOYSTICK_RAWINPUT_MATCH_AXES 185 int ii; 186 #endif 187 188 state->any_data = SDL_FALSE; 189 #ifdef SDL_JOYSTICK_RAWINPUT_MATCH_AXES 190 /* SHORT state->match_axes[4] = { 191 (match_state & 0x000F0000) >> 4, 192 (match_state & 0x00F00000) >> 8, 193 (match_state & 0x0F000000) >> 12, 194 (match_state & 0xF0000000) >> 16, 195 }; */ 196 for (ii = 0; ii < 4; ii++) { 197 state->match_axes[ii] = (match_state & (0x000F0000 << (ii * 4))) >> (4 + ii * 4); 198 if ((Uint32)(state->match_axes[ii] + 0x1000) > 0x2000) { /* match_state bit is not 0xF, 0x1, or 0x2 */ 199 state->any_data = SDL_TRUE; 200 } 201 } 202 #endif /* SDL_JOYSTICK_RAWINPUT_MATCH_AXES */ 203 204 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT 205 /* Match axes by checking if the distance between the high 4 bits of axis and the 4 bits from match_state is 1 or less */ 206 #define XInputAxesMatch(gamepad) (\ 207 (Uint32)(gamepad.sThumbLX - state->match_axes[0] + 0x1000) <= 0x2fff && \ 208 (Uint32)(~gamepad.sThumbLY - state->match_axes[1] + 0x1000) <= 0x2fff && \ 209 (Uint32)(gamepad.sThumbRX - state->match_axes[2] + 0x1000) <= 0x2fff && \ 210 (Uint32)(~gamepad.sThumbRY - state->match_axes[3] + 0x1000) <= 0x2fff) 211 /* Explicit 212 #define XInputAxesMatch(gamepad) (\ 213 SDL_abs((Sint8)((gamepad.sThumbLX & 0xF000) >> 8) - ((match_state & 0x000F0000) >> 12)) <= 0x10 && \ 214 SDL_abs((Sint8)((~gamepad.sThumbLY & 0xF000) >> 8) - ((match_state & 0x00F00000) >> 16)) <= 0x10 && \ 215 SDL_abs((Sint8)((gamepad.sThumbRX & 0xF000) >> 8) - ((match_state & 0x0F000000) >> 20)) <= 0x10 && \ 216 SDL_abs((Sint8)((~gamepad.sThumbRY & 0xF000) >> 8) - ((match_state & 0xF0000000) >> 24)) <= 0x10) */ 217 218 state->xinput_buttons = 219 /* Bitwise map .RLDUWVQTS.KYXBA -> YXBA..WVQTKSRLDU */ 220 match_state << 12 | (match_state & 0x0780) >> 1 | (match_state & 0x0010) << 1 | (match_state & 0x0040) >> 2 | (match_state & 0x7800) >> 11; 221 /* Explicit 222 ((match_state & (1<<SDL_CONTROLLER_BUTTON_A)) ? XINPUT_GAMEPAD_A : 0) | 223 ((match_state & (1<<SDL_CONTROLLER_BUTTON_B)) ? XINPUT_GAMEPAD_B : 0) | 224 ((match_state & (1<<SDL_CONTROLLER_BUTTON_X)) ? XINPUT_GAMEPAD_X : 0) | 225 ((match_state & (1<<SDL_CONTROLLER_BUTTON_Y)) ? XINPUT_GAMEPAD_Y : 0) | 226 ((match_state & (1<<SDL_CONTROLLER_BUTTON_BACK)) ? XINPUT_GAMEPAD_BACK : 0) | 227 ((match_state & (1<<SDL_CONTROLLER_BUTTON_START)) ? XINPUT_GAMEPAD_START : 0) | 228 ((match_state & (1<<SDL_CONTROLLER_BUTTON_LEFTSTICK)) ? XINPUT_GAMEPAD_LEFT_THUMB : 0) | 229 ((match_state & (1<<SDL_CONTROLLER_BUTTON_RIGHTSTICK)) ? XINPUT_GAMEPAD_RIGHT_THUMB: 0) | 230 ((match_state & (1<<SDL_CONTROLLER_BUTTON_LEFTSHOULDER)) ? XINPUT_GAMEPAD_LEFT_SHOULDER : 0) | 231 ((match_state & (1<<SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)) ? XINPUT_GAMEPAD_RIGHT_SHOULDER : 0) | 232 ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_UP)) ? XINPUT_GAMEPAD_DPAD_UP : 0) | 233 ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_DOWN)) ? XINPUT_GAMEPAD_DPAD_DOWN : 0) | 234 ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_LEFT)) ? XINPUT_GAMEPAD_DPAD_LEFT : 0) | 235 ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) ? XINPUT_GAMEPAD_DPAD_RIGHT : 0); 236 */ 237 238 if (state->xinput_buttons) 239 state->any_data = SDL_TRUE; 240 #endif 241 242 #ifdef SDL_JOYSTICK_RAWINPUT_WGI 243 /* Match axes by checking if the distance between the high 4 bits of axis and the 4 bits from match_state is 1 or less */ 244 #define WindowsGamingInputAxesMatch(gamepad) (\ 245 (Uint16)(((Sint16)(gamepad.LeftThumbstickX * SDL_MAX_SINT16) & 0xF000) - state->match_axes[0] + 0x1000) <= 0x2fff && \ 246 (Uint16)((~(Sint16)(gamepad.LeftThumbstickY * SDL_MAX_SINT16) & 0xF000) - state->match_axes[1] + 0x1000) <= 0x2fff && \ 247 (Uint16)(((Sint16)(gamepad.RightThumbstickX * SDL_MAX_SINT16) & 0xF000) - state->match_axes[2] + 0x1000) <= 0x2fff && \ 248 (Uint16)((~(Sint16)(gamepad.RightThumbstickY * SDL_MAX_SINT16) & 0xF000) - state->match_axes[3] + 0x1000) <= 0x2fff) 249 250 251 state->wgi_buttons = 252 /* Bitwise map .RLD UWVQ TS.K YXBA -> ..QT WVRL DUYX BAKS */ 253 /* RStick/LStick (QT) RShould/LShould (WV) DPad R/L/D/U YXBA bac(K) (S)tart */ 254 (match_state & 0x0180) << 5 | (match_state & 0x0600) << 1 | (match_state & 0x7800) >> 5 | (match_state & 0x000F) << 2 | (match_state & 0x0010) >> 3 | (match_state & 0x0040) >> 6; 255 /* Explicit 256 ((match_state & (1<<SDL_CONTROLLER_BUTTON_A)) ? GamepadButtons_A : 0) | 257 ((match_state & (1<<SDL_CONTROLLER_BUTTON_B)) ? GamepadButtons_B : 0) | 258 ((match_state & (1<<SDL_CONTROLLER_BUTTON_X)) ? GamepadButtons_X : 0) | 259 ((match_state & (1<<SDL_CONTROLLER_BUTTON_Y)) ? GamepadButtons_Y : 0) | 260 ((match_state & (1<<SDL_CONTROLLER_BUTTON_BACK)) ? GamepadButtons_View : 0) | 261 ((match_state & (1<<SDL_CONTROLLER_BUTTON_START)) ? GamepadButtons_Menu : 0) | 262 ((match_state & (1<<SDL_CONTROLLER_BUTTON_LEFTSTICK)) ? GamepadButtons_LeftThumbstick : 0) | 263 ((match_state & (1<<SDL_CONTROLLER_BUTTON_RIGHTSTICK)) ? GamepadButtons_RightThumbstick: 0) | 264 ((match_state & (1<<SDL_CONTROLLER_BUTTON_LEFTSHOULDER)) ? GamepadButtons_LeftShoulder: 0) | 265 ((match_state & (1<<SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)) ? GamepadButtons_RightShoulder: 0) | 266 ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_UP)) ? GamepadButtons_DPadUp : 0) | 267 ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_DOWN)) ? GamepadButtons_DPadDown : 0) | 268 ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_LEFT)) ? GamepadButtons_DPadLeft : 0) | 269 ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) ? GamepadButtons_DPadRight : 0); */ 270 271 if (state->wgi_buttons) 272 state->any_data = SDL_TRUE; 273 #endif 274 275 } 276 277 #endif /* SDL_JOYSTICK_RAWINPUT_MATCHING */ 278 279 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT 280 281 static struct { 282 XINPUT_STATE_EX state; 283 XINPUT_BATTERY_INFORMATION_EX battery; 284 SDL_bool connected; /* Currently has an active XInput device */ 285 SDL_bool used; /* Is currently mapped to an SDL device */ 286 Uint8 correlation_id; 287 } xinput_state[XUSER_MAX_COUNT]; 288 static SDL_bool xinput_device_change = SDL_TRUE; 289 static SDL_bool xinput_state_dirty = SDL_TRUE; 290 291 static void 292 RAWINPUT_UpdateXInput() 293 { 294 DWORD user_index; 295 if (xinput_device_change) { 296 for (user_index = 0; user_index < XUSER_MAX_COUNT; user_index++) { 297 XINPUT_CAPABILITIES capabilities; 298 xinput_state[user_index].connected = (XINPUTGETCAPABILITIES(user_index, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS) ? SDL_TRUE : SDL_FALSE; 299 } 300 xinput_device_change = SDL_FALSE; 301 xinput_state_dirty = SDL_TRUE; 302 } 303 if (xinput_state_dirty) { 304 xinput_state_dirty = SDL_FALSE; 305 for (user_index = 0; user_index < SDL_arraysize(xinput_state); ++user_index) { 306 if (xinput_state[user_index].connected) { 307 if (XINPUTGETSTATE(user_index, &xinput_state[user_index].state) != ERROR_SUCCESS) { 308 xinput_state[user_index].connected = SDL_FALSE; 309 } 310 xinput_state[user_index].battery.BatteryType = BATTERY_TYPE_UNKNOWN; 311 XINPUTGETBATTERYINFORMATION(user_index, BATTERY_DEVTYPE_GAMEPAD, &xinput_state[user_index].battery); 312 } 313 } 314 } 315 } 316 317 static void 318 RAWINPUT_MarkXInputSlotUsed(Uint8 xinput_slot) 319 { 320 if (xinput_slot != XUSER_INDEX_ANY) { 321 xinput_state[xinput_slot].used = SDL_TRUE; 322 } 323 } 324 325 static void 326 RAWINPUT_MarkXInputSlotFree(Uint8 xinput_slot) 327 { 328 if (xinput_slot != XUSER_INDEX_ANY) { 329 xinput_state[xinput_slot].used = SDL_FALSE; 330 } 331 } 332 static SDL_bool 333 RAWINPUT_MissingXInputSlot() 334 { 335 int ii; 336 for (ii = 0; ii < SDL_arraysize(xinput_state); ii++) { 337 if (xinput_state[ii].connected && !xinput_state[ii].used) { 338 return SDL_TRUE; 339 } 340 } 341 return SDL_FALSE; 342 } 343 344 static SDL_bool 345 RAWINPUT_XInputSlotMatches(const WindowsMatchState *state, Uint8 slot_idx) 346 { 347 if (xinput_state[slot_idx].connected) { 348 WORD xinput_buttons = xinput_state[slot_idx].state.Gamepad.wButtons; 349 if ((xinput_buttons & ~XINPUT_GAMEPAD_GUIDE) == state->xinput_buttons 350 #ifdef SDL_JOYSTICK_RAWINPUT_MATCH_AXES 351 && XInputAxesMatch(xinput_state[slot_idx].state.Gamepad) 352 #endif 353 ) { 354 return SDL_TRUE; 355 } 356 } 357 return SDL_FALSE; 358 } 359 360 361 static SDL_bool 362 RAWINPUT_GuessXInputSlot(const WindowsMatchState *state, Uint8 *correlation_id, Uint8 *slot_idx) 363 { 364 int user_index; 365 int match_count; 366 367 *slot_idx = 0; 368 369 match_count = 0; 370 for (user_index = 0; user_index < XUSER_MAX_COUNT; ++user_index) { 371 if (!xinput_state[user_index].used && RAWINPUT_XInputSlotMatches(state, user_index)) { 372 ++match_count; 373 *slot_idx = (Uint8)user_index; 374 /* Incrementing correlation_id for any match, as negative evidence for others being correlated */ 375 *correlation_id = ++xinput_state[user_index].correlation_id; 376 } 377 } 378 /* Only return a match if we match exactly one, and we have some non-zero data (buttons or axes) that matched. 379 Note that we're still invalidating *other* potential correlations if we have more than one match or we have no 380 data. */ 381 if (match_count == 1 && state->any_data) { 382 return SDL_TRUE; 383 } 384 return SDL_FALSE; 385 } 386 387 #endif /* SDL_JOYSTICK_RAWINPUT_XINPUT */ 388 389 #ifdef SDL_JOYSTICK_RAWINPUT_WGI 390 391 typedef struct WindowsGamingInputGamepadState { 392 __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad; 393 struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state; 394 RAWINPUT_DeviceContext *correlated_context; 395 SDL_bool used; /* Is currently mapped to an SDL device */ 396 SDL_bool connected; /* Just used during update to track disconnected */ 397 Uint8 correlation_id; 398 struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration; 399 } WindowsGamingInputGamepadState; 400 401 static struct { 402 WindowsGamingInputGamepadState **per_gamepad; 403 int per_gamepad_count; 404 SDL_bool initialized; 405 SDL_bool dirty; 406 SDL_bool need_device_list_update; 407 int ref_count; 408 __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics; 409 } wgi_state; 410 411 static void 412 RAWINPUT_MarkWindowsGamingInputSlotUsed(WindowsGamingInputGamepadState *wgi_slot, RAWINPUT_DeviceContext *ctx) 413 { 414 wgi_slot->used = SDL_TRUE; 415 wgi_slot->correlated_context = ctx; 416 } 417 418 static void 419 RAWINPUT_MarkWindowsGamingInputSlotFree(WindowsGamingInputGamepadState *wgi_slot) 420 { 421 wgi_slot->used = SDL_FALSE; 422 wgi_slot->correlated_context = NULL; 423 } 424 425 static SDL_bool 426 RAWINPUT_MissingWindowsGamingInputSlot() 427 { 428 int ii; 429 for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) { 430 if (!wgi_state.per_gamepad[ii]->used) { 431 return SDL_TRUE; 432 } 433 } 434 return SDL_FALSE; 435 } 436 437 static void 438 RAWINPUT_UpdateWindowsGamingInput() 439 { 440 int ii; 441 if (!wgi_state.gamepad_statics) 442 return; 443 444 if (!wgi_state.dirty) 445 return; 446 447 wgi_state.dirty = SDL_FALSE; 448 449 if (wgi_state.need_device_list_update) { 450 wgi_state.need_device_list_update = SDL_FALSE; 451 for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) { 452 wgi_state.per_gamepad[ii]->connected = SDL_FALSE; 453 } 454 HRESULT hr; 455 __FIVectorView_1_Windows__CGaming__CInput__CGamepad *gamepads; 456 457 hr = __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_get_Gamepads(wgi_state.gamepad_statics, &gamepads); 458 if (SUCCEEDED(hr)) { 459 unsigned int num_gamepads; 460 461 hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_get_Size(gamepads, &num_gamepads); 462 if (SUCCEEDED(hr)) { 463 unsigned int i; 464 for (i = 0; i < num_gamepads; ++i) { 465 __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad; 466 467 hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, i, &gamepad); 468 if (SUCCEEDED(hr)) { 469 SDL_bool found = SDL_FALSE; 470 int jj; 471 for (jj = 0; jj < wgi_state.per_gamepad_count ; jj++) { 472 if (wgi_state.per_gamepad[jj]->gamepad == gamepad) { 473 found = SDL_TRUE; 474 wgi_state.per_gamepad[jj]->connected = SDL_TRUE; 475 break; 476 } 477 } 478 if (!found) { 479 /* New device, add it */ 480 wgi_state.per_gamepad_count++; 481 wgi_state.per_gamepad = SDL_realloc(wgi_state.per_gamepad, sizeof(wgi_state.per_gamepad[0]) * wgi_state.per_gamepad_count); 482 if (!wgi_state.per_gamepad) { 483 SDL_OutOfMemory(); 484 return; 485 } 486 WindowsGamingInputGamepadState *gamepad_state = SDL_calloc(1, sizeof(*gamepad_state)); 487 if (!gamepad_state) { 488 SDL_OutOfMemory(); 489 return; 490 } 491 wgi_state.per_gamepad[wgi_state.per_gamepad_count - 1] = gamepad_state; 492 gamepad_state->gamepad = gamepad; 493 gamepad_state->connected = SDL_TRUE; 494 } else { 495 /* Already tracked */ 496 __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad); 497 } 498 } 499 } 500 for (ii = wgi_state.per_gamepad_count - 1; ii >= 0; ii--) { 501 WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[ii]; 502 if (!gamepad_state->connected) { 503 /* Device missing, must be disconnected */ 504 if (gamepad_state->correlated_context) { 505 gamepad_state->correlated_context->wgi_correlated = SDL_FALSE; 506 gamepad_state->correlated_context->wgi_slot = NULL; 507 } 508 __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad_state->gamepad); 509 SDL_free(gamepad_state); 510 wgi_state.per_gamepad[ii] = wgi_state.per_gamepad[wgi_state.per_gamepad_count - 1]; 511 --wgi_state.per_gamepad_count; 512 } 513 } 514 } 515 __FIVectorView_1_Windows__CGaming__CInput__CGamepad_Release(gamepads); 516 } 517 } /* need_device_list_update */ 518 519 for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) { 520 HRESULT hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(wgi_state.per_gamepad[ii]->gamepad, &wgi_state.per_gamepad[ii]->state); 521 if (!SUCCEEDED(hr)) { 522 wgi_state.per_gamepad[ii]->connected = SDL_FALSE; /* Not used by anything, currently */ 523 } 524 } 525 } 526 static void 527 RAWINPUT_InitWindowsGamingInput(RAWINPUT_DeviceContext *ctx) 528 { 529 wgi_state.need_device_list_update = SDL_TRUE; 530 wgi_state.ref_count++; 531 if (!wgi_state.initialized) { 532 /* I think this takes care of RoInitialize() in a way that is compatible with the rest of SDL */ 533 if (FAILED(WIN_CoInitialize())) { 534 return; 535 } 536 wgi_state.initialized = SDL_TRUE; 537 wgi_state.dirty = SDL_TRUE; 538 539 static const IID SDL_IID_IGamepadStatics = { 0x8BBCE529, 0xD49C, 0x39E9, { 0x95, 0x60, 0xE4, 0x7D, 0xDE, 0x96, 0xB7, 0xC8 } }; 540 HRESULT hr; 541 HMODULE hModule = LoadLibraryA("combase.dll"); 542 if (hModule != NULL) { 543 typedef HRESULT (WINAPI *WindowsCreateStringReference_t)(PCWSTR sourceString, UINT32 length, HSTRING_HEADER *hstringHeader, HSTRING* string); 544 typedef HRESULT (WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void** factory); 545 546 WindowsCreateStringReference_t WindowsCreateStringReferenceFunc = (WindowsCreateStringReference_t)GetProcAddress(hModule, "WindowsCreateStringReference"); 547 RoGetActivationFactory_t RoGetActivationFactoryFunc = (RoGetActivationFactory_t)GetProcAddress(hModule, "RoGetActivationFactory"); 548 if (WindowsCreateStringReferenceFunc && RoGetActivationFactoryFunc) { 549 LPTSTR pNamespace = L"Windows.Gaming.Input.Gamepad"; 550 HSTRING_HEADER hNamespaceStringHeader; 551 HSTRING hNamespaceString; 552 553 hr = WindowsCreateStringReferenceFunc(pNamespace, (UINT32)SDL_wcslen(pNamespace), &hNamespaceStringHeader, &hNamespaceString); 554 if (SUCCEEDED(hr)) { 555 RoGetActivationFactoryFunc(hNamespaceString, &SDL_IID_IGamepadStatics, &wgi_state.gamepad_statics); 556 } 557 } 558 FreeLibrary(hModule); 559 } 560 } 561 } 562 563 static SDL_bool 564 RAWINPUT_WindowsGamingInputSlotMatches(const WindowsMatchState *state, WindowsGamingInputGamepadState *slot) 565 { 566 Uint32 wgi_buttons = slot->state.Buttons; 567 if ((wgi_buttons & 0x3FFF) == state->wgi_buttons 568 #ifdef SDL_JOYSTICK_RAWINPUT_MATCH_AXES 569 && WindowsGamingInputAxesMatch(slot->state) 570 #endif 571 ) { 572 return SDL_TRUE; 573 } 574 return SDL_FALSE; 575 } 576 577 static SDL_bool 578 RAWINPUT_GuessWindowsGamingInputSlot(const WindowsMatchState *state, Uint8 *correlation_id, WindowsGamingInputGamepadState **slot) 579 { 580 int match_count, user_index; 581 582 match_count = 0; 583 for (user_index = 0; user_index < wgi_state.per_gamepad_count; ++user_index) { 584 WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[user_index]; 585 if (RAWINPUT_WindowsGamingInputSlotMatches(state, gamepad_state)) { 586 ++match_count; 587 *slot = gamepad_state; 588 /* Incrementing correlation_id for any match, as negative evidence for others being correlated */ 589 *correlation_id = ++gamepad_state->correlation_id; 590 } 591 } 592 /* Only return a match if we match exactly one, and we have some non-zero data (buttons or axes) that matched. 593 Note that we're still invalidating *other* potential correlations if we have more than one match or we have no 594 data. */ 595 if (match_count == 1 && state->any_data) { 596 return SDL_TRUE; 597 } 598 return SDL_FALSE; 599 } 600 601 static void 602 RAWINPUT_QuitWindowsGamingInput(RAWINPUT_DeviceContext *ctx) 603 { 604 wgi_state.need_device_list_update = SDL_TRUE; 605 --wgi_state.ref_count; 606 if (!wgi_state.ref_count && wgi_state.initialized) { 607 int ii; 608 for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) { 609 __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(wgi_state.per_gamepad[ii]->gamepad); 610 } 611 if (wgi_state.per_gamepad) { 612 SDL_free(wgi_state.per_gamepad); 613 wgi_state.per_gamepad = NULL; 614 } 615 wgi_state.per_gamepad_count = 0; 616 if (wgi_state.gamepad_statics) { 617 __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(wgi_state.gamepad_statics); 618 wgi_state.gamepad_statics = NULL; 619 } 620 WIN_CoUninitialize(); 621 wgi_state.initialized = SDL_FALSE; 622 } 623 } 624 625 #endif /* SDL_JOYSTICK_RAWINPUT_WGI */ 626 627 628 static SDL_RAWINPUT_Device * 629 RAWINPUT_AcquireDevice(SDL_RAWINPUT_Device *device) 630 { 631 SDL_AtomicIncRef(&device->refcount); 632 return device; 633 } 634 635 static void 636 RAWINPUT_ReleaseDevice(SDL_RAWINPUT_Device *device) 637 { 638 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT 639 if (device->joystick) { 640 RAWINPUT_DeviceContext *ctx = device->joystick->hwdata; 641 642 if (ctx->xinput_enabled && ctx->xinput_correlated) { 643 RAWINPUT_MarkXInputSlotFree(ctx->xinput_slot); 644 ctx->xinput_correlated = SDL_FALSE; 645 } 646 } 647 #endif /* SDL_JOYSTICK_RAWINPUT_XINPUT */ 648 649 if (SDL_AtomicDecRef(&device->refcount)) { 650 if (device->preparsed_data) { 651 SDL_HidD_FreePreparsedData(device->preparsed_data); 652 } 653 SDL_free(device->name); 654 SDL_free(device); 655 } 656 } 657 658 static SDL_RAWINPUT_Device * 659 RAWINPUT_DeviceFromHandle(HANDLE hDevice) 660 { 661 SDL_RAWINPUT_Device *curr; 662 663 for (curr = SDL_RAWINPUT_devices; curr; curr = curr->next) { 664 if (curr->hDevice == hDevice) 665 return curr; 666 } 667 return NULL; 668 } 669 670 static void 671 RAWINPUT_AddDevice(HANDLE hDevice) 672 { 673 #define CHECK(exp) { if(!(exp)) goto err; } 674 SDL_RAWINPUT_Device *device = NULL; 675 SDL_RAWINPUT_Device *curr, *last; 676 RID_DEVICE_INFO rdi; 677 UINT rdi_size = sizeof(rdi); 678 char dev_name[MAX_PATH]; 679 UINT name_size = SDL_arraysize(dev_name); 680 HANDLE hFile = INVALID_HANDLE_VALUE; 681 682 /* Make sure we're not trying to add the same device twice */ 683 if (RAWINPUT_DeviceFromHandle(hDevice)) { 684 return; 685 } 686 687 /* Figure out what kind of device it is */ 688 CHECK(GetRawInputDeviceInfoA(hDevice, RIDI_DEVICEINFO, &rdi, &rdi_size) != (UINT)-1); 689 CHECK(rdi.dwType == RIM_TYPEHID); 690 691 /* Get the device "name" (HID Path) */ 692 CHECK(GetRawInputDeviceInfoA(hDevice, RIDI_DEVICENAME, dev_name, &name_size) != (UINT)-1); 693 /* Only take XInput-capable devices */ 694 CHECK(SDL_strstr(dev_name, "IG_") != NULL); 695 #ifdef SDL_JOYSTICK_HIDAPI 696 /* Don't take devices handled by HIDAPI */ 697 CHECK(!HIDAPI_IsDevicePresent((Uint16)rdi.hid.dwVendorId, (Uint16)rdi.hid.dwProductId, (Uint16)rdi.hid.dwVersionNumber, "")); 698 #endif 699 700 CHECK(device = (SDL_RAWINPUT_Device *)SDL_calloc(1, sizeof(SDL_RAWINPUT_Device))); 701 device->hDevice = hDevice; 702 device->vendor_id = (Uint16)rdi.hid.dwVendorId; 703 device->product_id = (Uint16)rdi.hid.dwProductId; 704 device->version = (Uint16)rdi.hid.dwVersionNumber; 705 device->is_xinput = SDL_TRUE; 706 707 { 708 const Uint16 vendor = device->vendor_id; 709 const Uint16 product = device->product_id; 710 const Uint16 version = device->version; 711 Uint16 *guid16 = (Uint16 *)device->guid.data; 712 713 *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB); 714 *guid16++ = 0; 715 *guid16++ = SDL_SwapLE16(vendor); 716 *guid16++ = 0; 717 *guid16++ = SDL_SwapLE16(product); 718 *guid16++ = 0; 719 *guid16++ = SDL_SwapLE16(version); 720 *guid16++ = 0; 721 722 /* Note that this is a RAWINPUT device for special handling elsewhere */ 723 device->guid.data[14] = 'r'; 724 device->guid.data[15] = 0; 725 } 726 727 hFile = CreateFileA(dev_name, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); 728 CHECK(hFile != INVALID_HANDLE_VALUE); 729 730 { 731 char *manufacturer_string = NULL; 732 char *product_string = NULL; 733 WCHAR string[128]; 734 735 if (SDL_HidD_GetManufacturerString(hFile, string, sizeof(string))) { 736 manufacturer_string = WIN_StringToUTF8(string); 737 } 738 if (SDL_HidD_GetProductString(hFile, string, sizeof(string))) { 739 product_string = WIN_StringToUTF8(string); 740 } 741 742 device->name = SDL_CreateJoystickName(device->vendor_id, device->product_id, manufacturer_string, product_string); 743 744 if (manufacturer_string) { 745 SDL_free(manufacturer_string); 746 } 747 if (product_string) { 748 SDL_free(product_string); 749 } 750 } 751 752 CHECK(SDL_HidD_GetPreparsedData(hFile, &device->preparsed_data)); 753 754 CloseHandle(hFile); 755 hFile = INVALID_HANDLE_VALUE; 756 757 device->joystick_id = SDL_GetNextJoystickInstanceID(); 758 759 #ifdef DEBUG_RAWINPUT 760 SDL_Log("Adding RAWINPUT device '%s' VID 0x%.4x, PID 0x%.4x, version %d, handle 0x%.8x\n", device->name, device->vendor_id, device->product_id, device->version, device->hDevice); 761 #endif 762 763 /* Add it to the list */ 764 RAWINPUT_AcquireDevice(device); 765 for (curr = SDL_RAWINPUT_devices, last = NULL; curr; last = curr, curr = curr->next) { 766 continue; 767 } 768 if (last) { 769 last->next = device; 770 } else { 771 SDL_RAWINPUT_devices = device; 772 } 773 774 ++SDL_RAWINPUT_numjoysticks; 775 776 SDL_PrivateJoystickAdded(device->joystick_id); 777 778 return; 779 780 err: 781 if (hFile != INVALID_HANDLE_VALUE) { 782 CloseHandle(hFile); 783 } 784 if (device) { 785 if (device->name) 786 SDL_free(device->name); 787 SDL_free(device); 788 } 789 #undef CHECK 790 } 791 792 static void 793 RAWINPUT_DelDevice(SDL_RAWINPUT_Device *device, SDL_bool send_event) 794 { 795 SDL_RAWINPUT_Device *curr, *last; 796 for (curr = SDL_RAWINPUT_devices, last = NULL; curr; last = curr, curr = curr->next) { 797 if (curr == device) { 798 if (last) { 799 last->next = curr->next; 800 } else { 801 SDL_RAWINPUT_devices = curr->next; 802 } 803 --SDL_RAWINPUT_numjoysticks; 804 805 SDL_PrivateJoystickRemoved(device->joystick_id); 806 807 #ifdef DEBUG_RAWINPUT 808 SDL_Log("Removing RAWINPUT device '%s' VID 0x%.4x, PID 0x%.4x, version %d, handle %p\n", device->name, device->vendor_id, device->product_id, device->version, device->hDevice); 809 #endif 810 RAWINPUT_ReleaseDevice(device); 811 return; 812 } 813 } 814 } 815 816 static int 817 RAWINPUT_JoystickInit(void) 818 { 819 UINT device_count = 0; 820 821 SDL_assert(!SDL_RAWINPUT_inited); 822 823 if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_RAWINPUT, SDL_TRUE)) { 824 return -1; 825 } 826 827 if (WIN_LoadHIDDLL() < 0) { 828 return -1; 829 } 830 831 SDL_RAWINPUT_mutex = SDL_CreateMutex(); 832 SDL_RAWINPUT_inited = SDL_TRUE; 833 834 if ((GetRawInputDeviceList(NULL, &device_count, sizeof(RAWINPUTDEVICELIST)) != -1) && device_count > 0) { 835 PRAWINPUTDEVICELIST devices = NULL; 836 UINT i; 837 838 devices = (PRAWINPUTDEVICELIST)SDL_malloc(sizeof(RAWINPUTDEVICELIST) * device_count); 839 if (devices) { 840 if (GetRawInputDeviceList(devices, &device_count, sizeof(RAWINPUTDEVICELIST)) != -1) { 841 for (i = 0; i < device_count; ++i) { 842 RAWINPUT_AddDevice(devices[i].hDevice); 843 } 844 } 845 SDL_free(devices); 846 } 847 } 848 849 return 0; 850 } 851 852 static int 853 RAWINPUT_JoystickGetCount(void) 854 { 855 return SDL_RAWINPUT_numjoysticks; 856 } 857 858 SDL_bool 859 RAWINPUT_IsEnabled() 860 { 861 return SDL_RAWINPUT_inited; 862 } 863 864 SDL_bool 865 RAWINPUT_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) 866 { 867 SDL_RAWINPUT_Device *device; 868 869 /* If we're being asked about a device, that means another API just detected one, so rescan */ 870 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT 871 xinput_device_change = SDL_TRUE; 872 #endif 873 #ifdef SDL_JOYSTICK_RAWINPUT_WGI 874 wgi_state.need_device_list_update = SDL_TRUE; 875 #endif 876 877 device = SDL_RAWINPUT_devices; 878 while (device) { 879 if (vendor_id == device->vendor_id && product_id == device->product_id ) { 880 return SDL_TRUE; 881 } 882 883 /* The Xbox 360 wireless controller shows up as product 0 in WGI */ 884 if (vendor_id == device->vendor_id && product_id == 0 && 885 name && SDL_strstr(device->name, name) != NULL) { 886 return SDL_TRUE; 887 } 888 889 /* The Xbox One controller shows up as a hardcoded raw input VID/PID */ 890 if (name && SDL_strcmp(name, "Xbox One Game Controller") == 0 && 891 device->vendor_id == USB_VENDOR_MICROSOFT && 892 device->product_id == USB_PRODUCT_XBOX_ONE_RAW_INPUT_CONTROLLER) { 893 return SDL_TRUE; 894 } 895 896 device = device->next; 897 } 898 return SDL_FALSE; 899 } 900 901 static void 902 RAWINPUT_PostUpdate(void) 903 { 904 #ifdef SDL_JOYSTICK_RAWINPUT_MATCHING 905 SDL_bool unmapped_guide_pressed = SDL_FALSE; 906 907 #ifdef SDL_JOYSTICK_RAWINPUT_WGI 908 if (!wgi_state.dirty) { 909 int ii; 910 for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) { 911 WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[ii]; 912 if (!gamepad_state->used && (gamepad_state->state.Buttons & GamepadButtons_GUIDE)) { 913 unmapped_guide_pressed = SDL_TRUE; 914 break; 915 } 916 } 917 } 918 wgi_state.dirty = SDL_TRUE; 919 #endif 920 921 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT 922 if (!xinput_state_dirty) { 923 int ii; 924 for (ii = 0; ii < SDL_arraysize(xinput_state); ii++) { 925 if (xinput_state[ii].connected && !xinput_state[ii].used && (xinput_state[ii].state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE)) { 926 unmapped_guide_pressed = SDL_TRUE; 927 break; 928 } 929 } 930 } 931 xinput_state_dirty = SDL_TRUE; 932 #endif 933 934 if (unmapped_guide_pressed) { 935 if (guide_button_candidate.joystick && !guide_button_candidate.last_joystick) { 936 SDL_Joystick *joystick = guide_button_candidate.joystick; 937 RAWINPUT_DeviceContext *ctx = joystick->hwdata; 938 if (ctx->guide_hack) { 939 int guide_button = joystick->nbuttons - 1; 940 941 SDL_PrivateJoystickButton(guide_button_candidate.joystick, guide_button, SDL_PRESSED); 942 } 943 guide_button_candidate.last_joystick = guide_button_candidate.joystick; 944 } 945 } else if (guide_button_candidate.last_joystick) { 946 SDL_Joystick *joystick = guide_button_candidate.last_joystick; 947 RAWINPUT_DeviceContext *ctx = joystick->hwdata; 948 if (ctx->guide_hack) { 949 int guide_button = joystick->nbuttons - 1; 950 951 SDL_PrivateJoystickButton(joystick, guide_button, SDL_RELEASED); 952 } 953 guide_button_candidate.last_joystick = NULL; 954 } 955 guide_button_candidate.joystick = NULL; 956 957 #endif /* SDL_JOYSTICK_RAWINPUT_MATCHING */ 958 } 959 960 static void 961 RAWINPUT_JoystickDetect(void) 962 { 963 RAWINPUT_PostUpdate(); 964 } 965 966 static SDL_RAWINPUT_Device * 967 RAWINPUT_GetDeviceByIndex(int device_index) 968 { 969 SDL_RAWINPUT_Device *device = SDL_RAWINPUT_devices; 970 while (device) { 971 if (device_index == 0) { 972 break; 973 } 974 --device_index; 975 device = device->next; 976 } 977 return device; 978 } 979 980 static const char * 981 RAWINPUT_JoystickGetDeviceName(int device_index) 982 { 983 return RAWINPUT_GetDeviceByIndex(device_index)->name; 984 } 985 986 static int 987 RAWINPUT_JoystickGetDevicePlayerIndex(int device_index) 988 { 989 return -1; 990 } 991 992 static void 993 RAWINPUT_JoystickSetDevicePlayerIndex(int device_index, int player_index) 994 { 995 } 996 997 998 static SDL_JoystickGUID 999 RAWINPUT_JoystickGetDeviceGUID(int device_index) 1000 { 1001 return RAWINPUT_GetDeviceByIndex(device_index)->guid; 1002 } 1003 1004 static SDL_JoystickID 1005 RAWINPUT_JoystickGetDeviceInstanceID(int device_index) 1006 { 1007 return RAWINPUT_GetDeviceByIndex(device_index)->joystick_id; 1008 } 1009 1010 static int 1011 RAWINPUT_SortValueCaps(const void *A, const void *B) 1012 { 1013 HIDP_VALUE_CAPS *capsA = (HIDP_VALUE_CAPS *)A; 1014 HIDP_VALUE_CAPS *capsB = (HIDP_VALUE_CAPS *)B; 1015 1016 /* Sort by Usage for single values, or UsageMax for range of values */ 1017 return (int)capsA->NotRange.Usage - capsB->NotRange.Usage; 1018 } 1019 1020 static int 1021 RAWINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index) 1022 { 1023 SDL_RAWINPUT_Device *device = RAWINPUT_GetDeviceByIndex(device_index); 1024 RAWINPUT_DeviceContext *ctx; 1025 HIDP_CAPS caps; 1026 HIDP_BUTTON_CAPS *button_caps; 1027 HIDP_VALUE_CAPS *value_caps; 1028 ULONG i; 1029 1030 ctx = (RAWINPUT_DeviceContext *)SDL_calloc(1, sizeof(RAWINPUT_DeviceContext)); 1031 if (!ctx) { 1032 return SDL_OutOfMemory(); 1033 } 1034 joystick->hwdata = ctx; 1035 1036 ctx->device = RAWINPUT_AcquireDevice(device); 1037 device->joystick = joystick; 1038 1039 if (device->is_xinput) { 1040 /* We'll try to get guide button and trigger axes from XInput */ 1041 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT 1042 xinput_device_change = SDL_TRUE; 1043 ctx->xinput_enabled = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_CORRELATE_XINPUT, SDL_TRUE); 1044 if (ctx->xinput_enabled && (WIN_LoadXInputDLL() < 0 || !XINPUTGETSTATE)) { 1045 ctx->xinput_enabled = SDL_FALSE; 1046 } 1047 ctx->xinput_slot = XUSER_INDEX_ANY; 1048 #endif 1049 #ifdef SDL_JOYSTICK_RAWINPUT_WGI 1050 RAWINPUT_InitWindowsGamingInput(ctx); 1051 #endif 1052 } 1053 1054 ctx->is_xinput = device->is_xinput; 1055 ctx->preparsed_data = device->preparsed_data; 1056 ctx->max_data_length = SDL_HidP_MaxDataListLength(HidP_Input, ctx->preparsed_data); 1057 ctx->data = (HIDP_DATA *)SDL_malloc(ctx->max_data_length * sizeof(*ctx->data)); 1058 if (!ctx->data) { 1059 RAWINPUT_JoystickClose(joystick); 1060 return SDL_OutOfMemory(); 1061 } 1062 1063 if (SDL_HidP_GetCaps(ctx->preparsed_data, &caps) != HIDP_STATUS_SUCCESS) { 1064 RAWINPUT_JoystickClose(joystick); 1065 return SDL_SetError("Couldn't get device capabilities"); 1066 } 1067 1068 button_caps = SDL_stack_alloc(HIDP_BUTTON_CAPS, caps.NumberInputButtonCaps); 1069 if (SDL_HidP_GetButtonCaps(HidP_Input, button_caps, &caps.NumberInputButtonCaps, ctx->preparsed_data) != HIDP_STATUS_SUCCESS) { 1070 RAWINPUT_JoystickClose(joystick); 1071 return SDL_SetError("Couldn't get device button capabilities"); 1072 } 1073 1074 value_caps = SDL_stack_alloc(HIDP_VALUE_CAPS, caps.NumberInputValueCaps); 1075 if (SDL_HidP_GetValueCaps(HidP_Input, value_caps, &caps.NumberInputValueCaps, ctx->preparsed_data) != HIDP_STATUS_SUCCESS) { 1076 RAWINPUT_JoystickClose(joystick); 1077 return SDL_SetError("Couldn't get device value capabilities"); 1078 } 1079 1080 /* Sort the axes by usage, so X comes before Y, etc. */ 1081 SDL_qsort(value_caps, caps.NumberInputValueCaps, sizeof(*value_caps), RAWINPUT_SortValueCaps); 1082 1083 for (i = 0; i < caps.NumberInputButtonCaps; ++i) { 1084 HIDP_BUTTON_CAPS *cap = &button_caps[i]; 1085 1086 if (cap->UsagePage == USB_USAGEPAGE_BUTTON) { 1087 int count; 1088 1089 if (cap->IsRange) { 1090 count = 1 + (cap->Range.DataIndexMax - cap->Range.DataIndexMin); 1091 } else { 1092 count = 1; 1093 } 1094 1095 joystick->nbuttons += count; 1096 } 1097 } 1098 1099 if (joystick->nbuttons > 0) { 1100 int button_index = 0; 1101 1102 ctx->button_indices = (USHORT *)SDL_malloc(joystick->nbuttons * sizeof(*ctx->button_indices)); 1103 if (!ctx->button_indices) { 1104 RAWINPUT_JoystickClose(joystick); 1105 return SDL_OutOfMemory(); 1106 } 1107 1108 for (i = 0; i < caps.NumberInputButtonCaps; ++i) { 1109 HIDP_BUTTON_CAPS *cap = &button_caps[i]; 1110 1111 if (cap->UsagePage == USB_USAGEPAGE_BUTTON) { 1112 if (cap->IsRange) { 1113 int j, count = 1 + (cap->Range.DataIndexMax - cap->Range.DataIndexMin); 1114 1115 for (j = 0; j < count; ++j) { 1116 ctx->button_indices[button_index++] = cap->Range.DataIndexMin + j; 1117 } 1118 } else { 1119 ctx->button_indices[button_index++] = cap->NotRange.DataIndex; 1120 } 1121 } 1122 } 1123 } 1124 if (ctx->is_xinput && joystick->nbuttons == 10) { 1125 ctx->guide_hack = SDL_TRUE; 1126 joystick->nbuttons += 1; 1127 } 1128 1129 for (i = 0; i < caps.NumberInputValueCaps; ++i) { 1130 HIDP_VALUE_CAPS *cap = &value_caps[i]; 1131 1132 if (cap->IsRange) { 1133 continue; 1134 } 1135 1136 if (ctx->trigger_hack && cap->NotRange.Usage == USB_USAGE_GENERIC_Z) { 1137 continue; 1138 } 1139 1140 if (cap->NotRange.Usage == USB_USAGE_GENERIC_HAT) { 1141 joystick->nhats += 1; 1142 continue; 1143 } 1144 1145 if (ctx->is_xinput && cap->NotRange.Usage == USB_USAGE_GENERIC_Z) { 1146 continue; 1147 } 1148 1149 joystick->naxes += 1; 1150 } 1151 1152 if (joystick->naxes > 0) { 1153 int axis_index = 0; 1154 1155 ctx->axis_indices = (USHORT *)SDL_malloc(joystick->naxes * sizeof(*ctx->axis_indices)); 1156 if (!ctx->axis_indices) { 1157 RAWINPUT_JoystickClose(joystick); 1158 return SDL_OutOfMemory(); 1159 } 1160 1161 for (i = 0; i < caps.NumberInputValueCaps; ++i) { 1162 HIDP_VALUE_CAPS *cap = &value_caps[i]; 1163 1164 if (cap->IsRange) { 1165 continue; 1166 } 1167 1168 if (cap->NotRange.Usage == USB_USAGE_GENERIC_HAT) { 1169 continue; 1170 } 1171 1172 if (ctx->is_xinput && cap->NotRange.Usage == USB_USAGE_GENERIC_Z) { 1173 ctx->trigger_hack = SDL_TRUE; 1174 ctx->trigger_hack_index = cap->NotRange.DataIndex; 1175 continue; 1176 } 1177 1178 ctx->axis_indices[axis_index++] = cap->NotRange.DataIndex; 1179 } 1180 } 1181 if (ctx->trigger_hack) { 1182 joystick->naxes += 2; 1183 } 1184 1185 if (joystick->nhats > 0) { 1186 int hat_index = 0; 1187 1188 ctx->hat_indices = (USHORT *)SDL_malloc(joystick->nhats * sizeof(*ctx->hat_indices)); 1189 if (!ctx->hat_indices) { 1190 RAWINPUT_JoystickClose(joystick); 1191 return SDL_OutOfMemory(); 1192 } 1193 1194 for (i = 0; i < caps.NumberInputValueCaps; ++i) { 1195 HIDP_VALUE_CAPS *cap = &value_caps[i]; 1196 1197 if (cap->IsRange) { 1198 continue; 1199 } 1200 1201 if (cap->NotRange.Usage != USB_USAGE_GENERIC_HAT) { 1202 continue; 1203 } 1204 1205 ctx->hat_indices[hat_index++] = cap->NotRange.DataIndex; 1206 } 1207 } 1208 1209 SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_UNKNOWN); 1210 1211 return 0; 1212 } 1213 1214 static int 1215 RAWINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 1216 { 1217 #if defined(SDL_JOYSTICK_RAWINPUT_WGI) || defined(SDL_JOYSTICK_RAWINPUT_XINPUT) 1218 RAWINPUT_DeviceContext *ctx = joystick->hwdata; 1219 #endif 1220 1221 SDL_bool rumbled = SDL_FALSE; 1222 1223 #ifdef SDL_JOYSTICK_RAWINPUT_WGI 1224 if (!rumbled && ctx->wgi_correlated) { 1225 WindowsGamingInputGamepadState *gamepad_state = ctx->wgi_slot; 1226 HRESULT hr; 1227 gamepad_state->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16; 1228 gamepad_state->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16; 1229 hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(gamepad_state->gamepad, gamepad_state->vibration); 1230 if (SUCCEEDED(hr)) { 1231 rumbled = SDL_TRUE; 1232 } 1233 } 1234 #endif 1235 1236 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT 1237 if (!rumbled && ctx->xinput_correlated) { 1238 XINPUT_VIBRATION XVibration; 1239 1240 if (!XINPUTSETSTATE) { 1241 return SDL_Unsupported(); 1242 } 1243 1244 XVibration.wLeftMotorSpeed = low_frequency_rumble; 1245 XVibration.wRightMotorSpeed = high_frequency_rumble; 1246 if (XINPUTSETSTATE(ctx->xinput_slot, &XVibration) == ERROR_SUCCESS) { 1247 rumbled = SDL_TRUE; 1248 } else { 1249 return SDL_SetError("XInputSetState() failed"); 1250 } 1251 } 1252 #endif /* SDL_JOYSTICK_RAWINPUT_XINPUT */ 1253 1254 return 0; 1255 } 1256 1257 static int 1258 RAWINPUT_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) 1259 { 1260 #if defined(SDL_JOYSTICK_RAWINPUT_WGI) 1261 RAWINPUT_DeviceContext *ctx = joystick->hwdata; 1262 1263 if (ctx->wgi_correlated) { 1264 WindowsGamingInputGamepadState *gamepad_state = ctx->wgi_slot; 1265 HRESULT hr; 1266 gamepad_state->vibration.LeftTrigger = (DOUBLE)left_rumble / SDL_MAX_UINT16; 1267 gamepad_state->vibration.RightTrigger = (DOUBLE)right_rumble / SDL_MAX_UINT16; 1268 hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(gamepad_state->gamepad, gamepad_state->vibration); 1269 if (!SUCCEEDED(hr)) { 1270 return SDL_SetError("Setting vibration failed: 0x%x\n", hr); 1271 } 1272 } 1273 return 0; 1274 #else 1275 return SDL_Unsupported(); 1276 #endif 1277 } 1278 1279 static SDL_bool 1280 RAWINPUT_JoystickHasLED(SDL_Joystick *joystick) 1281 { 1282 return SDL_FALSE; 1283 } 1284 1285 static int 1286 RAWINPUT_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) 1287 { 1288 return SDL_Unsupported(); 1289 } 1290 1291 static int 1292 RAWINPUT_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) 1293 { 1294 return SDL_Unsupported(); 1295 } 1296 1297 static HIDP_DATA *GetData(USHORT index, HIDP_DATA *data, ULONG length) 1298 { 1299 ULONG i; 1300 1301 /* Check to see if the data is at the expected offset */ 1302 if (index < length && data[index].DataIndex == index) { 1303 return &data[index]; 1304 } 1305 1306 /* Loop through the data to find it */ 1307 for (i = 0; i < length; ++i) { 1308 if (data[i].DataIndex == index) { 1309 return &data[i]; 1310 } 1311 } 1312 return NULL; 1313 } 1314 1315 /* This is the packet format for Xbox 360 and Xbox One controllers on Windows, 1316 however with this interface there is no rumble support, no guide button, 1317 and the left and right triggers are tied together as a single axis. 1318 1319 We use XInput and Windows.Gaming.Input to make up for these shortcomings. 1320 */ 1321 static void 1322 RAWINPUT_HandleStatePacket(SDL_Joystick *joystick, Uint8 *data, int size) 1323 { 1324 RAWINPUT_DeviceContext *ctx = joystick->hwdata; 1325 #ifdef SDL_JOYSTICK_RAWINPUT_MATCHING 1326 /* Map new buttons and axes into game controller controls */ 1327 static const int button_map[] = { 1328 SDL_CONTROLLER_BUTTON_A, 1329 SDL_CONTROLLER_BUTTON_B, 1330 SDL_CONTROLLER_BUTTON_X, 1331 SDL_CONTROLLER_BUTTON_Y, 1332 SDL_CONTROLLER_BUTTON_LEFTSHOULDER, 1333 SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, 1334 SDL_CONTROLLER_BUTTON_BACK, 1335 SDL_CONTROLLER_BUTTON_START, 1336 SDL_CONTROLLER_BUTTON_LEFTSTICK, 1337 SDL_CONTROLLER_BUTTON_RIGHTSTICK 1338 }; 1339 #define HAT_MASK ((1 << SDL_CONTROLLER_BUTTON_DPAD_UP) | (1 << SDL_CONTROLLER_BUTTON_DPAD_DOWN) | (1 << SDL_CONTROLLER_BUTTON_DPAD_LEFT) | (1 << SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) 1340 static const int hat_map[] = { 1341 0, 1342 (1 << SDL_CONTROLLER_BUTTON_DPAD_UP), 1343 (1 << SDL_CONTROLLER_BUTTON_DPAD_UP) | (1 << SDL_CONTROLLER_BUTTON_DPAD_RIGHT), 1344 (1 << SDL_CONTROLLER_BUTTON_DPAD_RIGHT), 1345 (1 << SDL_CONTROLLER_BUTTON_DPAD_DOWN) | (1 << SDL_CONTROLLER_BUTTON_DPAD_RIGHT), 1346 (1 << SDL_CONTROLLER_BUTTON_DPAD_DOWN), 1347 (1 << SDL_CONTROLLER_BUTTON_DPAD_DOWN) | (1 << SDL_CONTROLLER_BUTTON_DPAD_LEFT), 1348 (1 << SDL_CONTROLLER_BUTTON_DPAD_LEFT), 1349 (1 << SDL_CONTROLLER_BUTTON_DPAD_UP) | (1 << SDL_CONTROLLER_BUTTON_DPAD_LEFT), 1350 }; 1351 Uint32 match_state = ctx->match_state; 1352 /* Update match_state with button bit, then fall through */ 1353 #define SDL_PrivateJoystickButton(joystick, button, state) if (button < SDL_arraysize(button_map)) { if (state) match_state |= 1 << button_map[button]; else match_state &= ~(1 << button_map[button]); } SDL_PrivateJoystickButton(joystick, button, state) 1354 #ifdef SDL_JOYSTICK_RAWINPUT_MATCH_AXES 1355 /* Grab high 4 bits of value, then fall through */ 1356 #define SDL_PrivateJoystickAxis(joystick, axis, value) if (axis < 4) match_state = (match_state & ~(0xF << (4 * axis + 16))) | ((value) & 0xF000) << (4 * axis + 4); SDL_PrivateJoystickAxis(joystick, axis, value) 1357 #endif 1358 #endif /* SDL_JOYSTICK_RAWINPUT_MATCHING */ 1359 1360 ULONG data_length = ctx->max_data_length; 1361 int i; 1362 int nbuttons = joystick->nbuttons - (ctx->guide_hack * 1); 1363 int naxes = joystick->naxes - (ctx->trigger_hack * 2); 1364 int nhats = joystick->nhats; 1365 Uint32 button_mask = 0; 1366 1367 if (SDL_HidP_GetData(HidP_Input, ctx->data, &data_length, ctx->preparsed_data, (PCHAR)data, size) != HIDP_STATUS_SUCCESS) { 1368 return; 1369 } 1370 1371 for (i = 0; i < nbuttons; ++i) { 1372 HIDP_DATA *item = GetData(ctx->button_indices[i], ctx->data, data_length); 1373 if (item && item->On) { 1374 button_mask |= (1 << i); 1375 } 1376 } 1377 for (i = 0; i < nbuttons; ++i) { 1378 SDL_PrivateJoystickButton(joystick, i, (button_mask & (1 << i)) ? SDL_PRESSED : SDL_RELEASED); 1379 } 1380 1381 for (i = 0; i < naxes; ++i) { 1382 HIDP_DATA *item = GetData(ctx->axis_indices[i], ctx->data, data_length); 1383 if (item) { 1384 Sint16 axis = (int)(Uint16)item->RawValue - 0x8000; 1385 SDL_PrivateJoystickAxis(joystick, i, axis); 1386 } 1387 } 1388 1389 for (i = 0; i < nhats; ++i) { 1390 HIDP_DATA *item = GetData(ctx->hat_indices[i], ctx->data, data_length); 1391 if (item) { 1392 const Uint8 hat_states[] = { 1393 SDL_HAT_CENTERED, 1394 SDL_HAT_UP, 1395 SDL_HAT_UP | SDL_HAT_RIGHT, 1396 SDL_HAT_RIGHT, 1397 SDL_HAT_DOWN | SDL_HAT_RIGHT, 1398 SDL_HAT_DOWN, 1399 SDL_HAT_DOWN | SDL_HAT_LEFT, 1400 SDL_HAT_LEFT, 1401 SDL_HAT_UP | SDL_HAT_LEFT, 1402 }; 1403 ULONG state = item->RawValue; 1404 1405 if (state < SDL_arraysize(hat_states)) { 1406 #ifdef SDL_JOYSTICK_RAWINPUT_MATCHING 1407 match_state = (match_state & ~HAT_MASK) | hat_map[state]; 1408 #endif 1409 SDL_PrivateJoystickHat(joystick, i, hat_states[state]); 1410 } 1411 } 1412 } 1413 1414 #ifdef SDL_PrivateJoystickButton 1415 #undef SDL_PrivateJoystickButton 1416 #endif 1417 #ifdef SDL_PrivateJoystickAxis 1418 #undef SDL_PrivateJoystickAxis 1419 #endif 1420 1421 if (ctx->trigger_hack) { 1422 SDL_bool has_trigger_data = SDL_FALSE; 1423 1424 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT 1425 /* Prefer XInput over WindowsGamingInput, it continues to provide data in the background */ 1426 if (!has_trigger_data && ctx->xinput_enabled && ctx->xinput_correlated) { 1427 has_trigger_data = SDL_TRUE; 1428 } 1429 #endif /* SDL_JOYSTICK_RAWINPUT_XINPUT */ 1430 1431 #ifdef SDL_JOYSTICK_RAWINPUT_WGI 1432 if (!has_trigger_data && ctx->wgi_correlated) { 1433 has_trigger_data = SDL_TRUE; 1434 } 1435 #endif /* SDL_JOYSTICK_RAWINPUT_WGI */ 1436 1437 if (!has_trigger_data) { 1438 HIDP_DATA *item = GetData(ctx->trigger_hack_index, ctx->data, data_length); 1439 if (item) { 1440 int left_trigger = joystick->naxes - 2; 1441 int right_trigger = joystick->naxes - 1; 1442 Sint16 value = (int)(Uint16)item->RawValue - 0x8000; 1443 if (value < 0) { 1444 value = -value * 2 - 32769; 1445 SDL_PrivateJoystickAxis(joystick, left_trigger, SDL_MIN_SINT16); 1446 SDL_PrivateJoystickAxis(joystick, right_trigger, value); 1447 } else if (value > 0) { 1448 value = value * 2 - 32767; 1449 SDL_PrivateJoystickAxis(joystick, left_trigger, value); 1450 SDL_PrivateJoystickAxis(joystick, right_trigger, SDL_MIN_SINT16); 1451 } else { 1452 SDL_PrivateJoystickAxis(joystick, left_trigger, SDL_MIN_SINT16); 1453 SDL_PrivateJoystickAxis(joystick, right_trigger, SDL_MIN_SINT16); 1454 } 1455 } 1456 } 1457 } 1458 1459 #ifdef SDL_JOYSTICK_RAWINPUT_MATCHING 1460 if (ctx->is_xinput) { 1461 ctx->match_state = match_state; 1462 ctx->last_state_packet = SDL_GetTicks(); 1463 } 1464 #endif 1465 } 1466 1467 static void 1468 RAWINPUT_UpdateOtherAPIs(SDL_Joystick *joystick) 1469 { 1470 #ifdef SDL_JOYSTICK_RAWINPUT_MATCHING 1471 RAWINPUT_DeviceContext *ctx = joystick->hwdata; 1472 SDL_bool has_trigger_data = SDL_FALSE; 1473 SDL_bool correlated = SDL_FALSE; 1474 WindowsMatchState match_state_xinput; 1475 int guide_button = joystick->nbuttons - 1; 1476 int left_trigger = joystick->naxes - 2; 1477 int right_trigger = joystick->naxes - 1; 1478 1479 RAWINPUT_FillMatchState(&match_state_xinput, ctx->match_state); 1480 1481 #ifdef SDL_JOYSTICK_RAWINPUT_WGI 1482 /* Parallel logic to WINDOWS_XINPUT below */ 1483 RAWINPUT_UpdateWindowsGamingInput(); 1484 if (ctx->wgi_correlated) { 1485 /* We have been previously correlated, ensure we are still matching, see comments in XINPUT section */ 1486 if (RAWINPUT_WindowsGamingInputSlotMatches(&match_state_xinput, ctx->wgi_slot)) { 1487 ctx->wgi_uncorrelate_count = 0; 1488 } else { 1489 ++ctx->wgi_uncorrelate_count; 1490 /* Only un-correlate if this is consistent over multiple Update() calls - the timing of polling/event 1491 pumping can easily cause this to uncorrelate for a frame. 2 seemed reliable in my testing, but 1492 let's set it to 3 to be safe. An incorrect un-correlation will simply result in lower precision 1493 triggers for a frame. */ 1494 if (ctx->wgi_uncorrelate_count >= 3) { 1495 #ifdef DEBUG_RAWINPUT 1496 SDL_Log("UN-Correlated joystick %d to WindowsGamingInput device #%d\n", joystick->instance_id, ctx->wgi_slot); 1497 #endif 1498 RAWINPUT_MarkWindowsGamingInputSlotFree(ctx->wgi_slot); 1499 ctx->wgi_correlated = SDL_FALSE; 1500 ctx->wgi_correlation_count = 0; 1501 /* Force release of Guide button, it can't possibly be down on this device now. */ 1502 /* It gets left down if we were actually correlated incorrectly and it was released on the WindowsGamingInput 1503 device but we didn't get a state packet. */ 1504 if (ctx->guide_hack) { 1505 SDL_PrivateJoystickButton(joystick, guide_button, SDL_RELEASED); 1506 } 1507 } 1508 } 1509 } 1510 if (!ctx->wgi_correlated) { 1511 SDL_bool new_correlation_count = 0; 1512 if (RAWINPUT_MissingWindowsGamingInputSlot()) { 1513 Uint8 correlation_id; 1514 WindowsGamingInputGamepadState *slot_idx; 1515 if (RAWINPUT_GuessWindowsGamingInputSlot(&match_state_xinput, &correlation_id, &slot_idx)) { 1516 /* we match exactly one WindowsGamingInput device */ 1517 /* Probably can do without wgi_correlation_count, just check and clear wgi_slot to NULL, unless we need 1518 even more frames to be sure. */ 1519 if (ctx->wgi_correlation_count && ctx->wgi_slot == slot_idx) { 1520 /* was correlated previously, and still the same device */ 1521 if (ctx->wgi_correlation_id + 1 == correlation_id) { 1522 /* no one else was correlated in the meantime */ 1523 new_correlation_count = ctx->wgi_correlation_count + 1; 1524 if (new_correlation_count == 2) { 1525 /* correlation stayed steady and uncontested across multiple frames, guaranteed match */ 1526 ctx->wgi_correlated = SDL_TRUE; 1527 #ifdef DEBUG_RAWINPUT 1528 SDL_Log("Correlated joystick %d to WindowsGamingInput device #%d\n", joystick->instance_id, slot_idx); 1529 #endif 1530 correlated = SDL_TRUE; 1531 RAWINPUT_MarkWindowsGamingInputSlotUsed(ctx->wgi_slot, ctx); 1532 /* If the generalized Guide button was using us, it doesn't need to anymore */ 1533 if (guide_button_candidate.joystick == joystick) 1534 guide_button_candidate.joystick = NULL; 1535 if (guide_button_candidate.last_joystick == joystick) 1536 guide_button_candidate.last_joystick = NULL; 1537 } 1538 } else { 1539 /* someone else also possibly correlated to this device, start over */ 1540 new_correlation_count = 1; 1541 } 1542 } else { 1543 /* new possible correlation */ 1544 new_correlation_count = 1; 1545 ctx->wgi_slot = slot_idx; 1546 } 1547 ctx->wgi_correlation_id = correlation_id; 1548 } else { 1549 /* Match multiple WindowsGamingInput devices, or none (possibly due to no buttons pressed) */ 1550 } 1551 } 1552 ctx->wgi_correlation_count = new_correlation_count; 1553 } else { 1554 correlated = SDL_TRUE; 1555 } 1556 #endif /* SDL_JOYSTICK_RAWINPUT_WGI */ 1557 1558 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT 1559 /* Parallel logic to WINDOWS_GAMING_INPUT above */ 1560 if (ctx->xinput_enabled) { 1561 RAWINPUT_UpdateXInput(); 1562 if (ctx->xinput_correlated) { 1563 /* We have been previously correlated, ensure we are still matching */ 1564 /* This is required to deal with two (mostly) un-preventable mis-correlation situations: 1565 A) Since the HID data stream does not provide an initial state (but polling XInput does), if we open 1566 5 controllers (#1-4 XInput mapped, #5 is not), and controller 1 had the A button down (and we don't 1567 know), and the user presses A on controller #5, we'll see exactly 1 controller with A down (#5) and 1568 exactly 1 XInput device with A down (#1), and incorrectly correlate. This code will then un-correlate 1569 when A is released from either controller #1 or #5. 1570 B) Since the app may not open all controllers, we could have a similar situation where only controller #5 1571 is opened, and the user holds A on controllers #1 and #5 simultaneously - again we see only 1 controller 1572 with A down and 1 XInput device with A down, and incorrectly correlate. This should be very unusual 1573 (only when apps do not open all controllers, yet are listening to Guide button presses, yet 1574 for some reason want to ignore guide button presses on the un-opened controllers, yet users are 1575 pressing buttons on the unopened controllers), and will resolve itself when either button is released 1576 and we un-correlate. We could prevent this by processing the state packets for *all* controllers, 1577 even un-opened ones, as that would allow more precise correlation. 1578 */ 1579 if (RAWINPUT_XInputSlotMatches(&match_state_xinput, ctx->xinput_slot)) { 1580 ctx->xinput_uncorrelate_count = 0; 1581 } else { 1582 ++ctx->xinput_uncorrelate_count; 1583 /* Only un-correlate if this is consistent over multiple Update() calls - the timing of polling/event 1584 pumping can easily cause this to uncorrelate for a frame. 2 seemed reliable in my testing, but 1585 let's set it to 3 to be safe. An incorrect un-correlation will simply result in lower precision 1586 triggers for a frame. */ 1587 if (ctx->xinput_uncorrelate_count >= 3) { 1588 #ifdef DEBUG_RAWINPUT 1589 SDL_Log("UN-Correlated joystick %d to XInput device #%d\n", joystick->instance_id, ctx->xinput_slot); 1590 #endif 1591 RAWINPUT_MarkXInputSlotFree(ctx->xinput_slot); 1592 ctx->xinput_correlated = SDL_FALSE; 1593 ctx->xinput_correlation_count = 0; 1594 /* Force release of Guide button, it can't possibly be down on this device now. */ 1595 /* It gets left down if we were actually correlated incorrectly and it was released on the XInput 1596 device but we didn't get a state packet. */ 1597 if (ctx->guide_hack) { 1598 SDL_PrivateJoystickButton(joystick, guide_button, SDL_RELEASED); 1599 } 1600 } 1601 } 1602 } 1603 if (!ctx->xinput_correlated) { 1604 Uint8 new_correlation_count = 0; 1605 if (RAWINPUT_MissingXInputSlot()) { 1606 Uint8 correlation_id = 0; 1607 Uint8 slot_idx = 0; 1608 if (RAWINPUT_GuessXInputSlot(&match_state_xinput, &correlation_id, &slot_idx)) { 1609 /* we match exactly one XInput device */ 1610 /* Probably can do without xinput_correlation_count, just check and clear xinput_slot to ANY, unless 1611 we need even more frames to be sure */ 1612 if (ctx->xinput_correlation_count && ctx->xinput_slot == slot_idx) { 1613 /* was correlated previously, and still the same device */ 1614 if (ctx->xinput_correlation_id + 1 == correlation_id) { 1615 /* no one else was correlated in the meantime */ 1616 new_correlation_count = ctx->xinput_correlation_count + 1; 1617 if (new_correlation_count == 2) { 1618 /* correlation stayed steady and uncontested across multiple frames, guaranteed match */ 1619 ctx->xinput_correlated = SDL_TRUE; 1620 #ifdef DEBUG_RAWINPUT 1621 SDL_Log("Correlated joystick %d to XInput device #%d\n", joystick->instance_id, slot_idx); 1622 #endif 1623 correlated = SDL_TRUE; 1624 RAWINPUT_MarkXInputSlotUsed(ctx->xinput_slot); 1625 /* If the generalized Guide button was using us, it doesn't need to anymore */ 1626 if (guide_button_candidate.joystick == joystick) 1627 guide_button_candidate.joystick = NULL; 1628 if (guide_button_candidate.last_joystick == joystick) 1629 guide_button_candidate.last_joystick = NULL; 1630 } 1631 } else { 1632 /* someone else also possibly correlated to this device, start over */ 1633 new_correlation_count = 1; 1634 } 1635 } else { 1636 /* new possible correlation */ 1637 new_correlation_count = 1; 1638 ctx->xinput_slot = slot_idx; 1639 } 1640 ctx->xinput_correlation_id = correlation_id; 1641 } else { 1642 /* Match multiple XInput devices, or none (possibly due to no buttons pressed) */ 1643 } 1644 } 1645 ctx->xinput_correlation_count = new_correlation_count; 1646 } else { 1647 correlated = SDL_TRUE; 1648 } 1649 } 1650 #endif /* SDL_JOYSTICK_RAWINPUT_XINPUT */ 1651 1652 /* Poll for trigger data once (not per-state-packet) */ 1653 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT 1654 /* Prefer XInput over WindowsGamingInput, it continues to provide data in the background */ 1655 if (!has_trigger_data && ctx->xinput_enabled && ctx->xinput_correlated) { 1656 RAWINPUT_UpdateXInput(); 1657 if (xinput_state[ctx->xinput_slot].connected) { 1658 XINPUT_BATTERY_INFORMATION_EX *battery_info = &xinput_state[ctx->xinput_slot].battery; 1659 1660 if (ctx->guide_hack) { 1661 SDL_PrivateJoystickButton(joystick, guide_button, (xinput_state[ctx->xinput_slot].state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE) ? SDL_PRESSED : SDL_RELEASED); 1662 } 1663 if (ctx->trigger_hack) { 1664 SDL_PrivateJoystickAxis(joystick, left_trigger, ((int)xinput_state[ctx->xinput_slot].state.Gamepad.bLeftTrigger * 257) - 32768); 1665 SDL_PrivateJoystickAxis(joystick, right_trigger, ((int)xinput_state[ctx->xinput_slot].state.Gamepad.bRightTrigger * 257) - 32768); 1666 } 1667 has_trigger_data = SDL_TRUE; 1668 1669 if (battery_info->BatteryType != BATTERY_TYPE_UNKNOWN) { 1670 SDL_JoystickPowerLevel ePowerLevel = SDL_JOYSTICK_POWER_UNKNOWN; 1671 if (battery_info->BatteryType == BATTERY_TYPE_WIRED) { 1672 ePowerLevel = SDL_JOYSTICK_POWER_WIRED; 1673 } else { 1674 switch (battery_info->BatteryLevel) { 1675 case BATTERY_LEVEL_EMPTY: 1676 ePowerLevel = SDL_JOYSTICK_POWER_EMPTY; 1677 break; 1678 case BATTERY_LEVEL_LOW: 1679 ePowerLevel = SDL_JOYSTICK_POWER_LOW; 1680 break; 1681 case BATTERY_LEVEL_MEDIUM: 1682 ePowerLevel = SDL_JOYSTICK_POWER_MEDIUM; 1683 break; 1684 default: 1685 case BATTERY_LEVEL_FULL: 1686 ePowerLevel = SDL_JOYSTICK_POWER_FULL; 1687 break; 1688 } 1689 } 1690 SDL_PrivateJoystickBatteryLevel(joystick, ePowerLevel); 1691 } 1692 } 1693 } 1694 #endif /* SDL_JOYSTICK_RAWINPUT_XINPUT */ 1695 1696 #ifdef SDL_JOYSTICK_RAWINPUT_WGI 1697 if (!has_trigger_data && ctx->wgi_correlated) { 1698 RAWINPUT_UpdateWindowsGamingInput(); /* May detect disconnect / cause uncorrelation */ 1699 if (ctx->wgi_correlated) { /* Still connected */ 1700 struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading *state = &ctx->wgi_slot->state; 1701 1702 if (ctx->guide_hack) { 1703 SDL_PrivateJoystickButton(joystick, guide_button, (state->Buttons & GamepadButtons_GUIDE) ? SDL_PRESSED : SDL_RELEASED); 1704 } 1705 if (ctx->trigger_hack) { 1706 SDL_PrivateJoystickAxis(joystick, left_trigger, ((int)(state->LeftTrigger * SDL_MAX_UINT16)) - 32768); 1707 SDL_PrivateJoystickAxis(joystick, right_trigger, ((int)(state->RightTrigger * SDL_MAX_UINT16)) - 32768); 1708 } 1709 has_trigger_data = SDL_TRUE; 1710 } 1711 } 1712 #endif /* SDL_JOYSTICK_RAWINPUT_WGI */ 1713 1714 if (!correlated) { 1715 if (!guide_button_candidate.joystick || 1716 (ctx->last_state_packet && ( 1717 !guide_button_candidate.last_state_packet || 1718 SDL_TICKS_PASSED(ctx->last_state_packet, guide_button_candidate.last_state_packet) 1719 )) 1720 ) { 1721 guide_button_candidate.joystick = joystick; 1722 guide_button_candidate.last_state_packet = ctx->last_state_packet; 1723 } 1724 } 1725 #endif /* SDL_JOYSTICK_RAWINPUT_MATCHING */ 1726 } 1727 1728 static void 1729 RAWINPUT_JoystickUpdate(SDL_Joystick *joystick) 1730 { 1731 RAWINPUT_UpdateOtherAPIs(joystick); 1732 } 1733 1734 static void 1735 RAWINPUT_JoystickClose(SDL_Joystick *joystick) 1736 { 1737 RAWINPUT_DeviceContext *ctx = joystick->hwdata; 1738 1739 #ifdef SDL_JOYSTICK_RAWINPUT_MATCHING 1740 if (guide_button_candidate.joystick == joystick) 1741 guide_button_candidate.joystick = NULL; 1742 if (guide_button_candidate.last_joystick == joystick) 1743 guide_button_candidate.last_joystick = NULL; 1744 #endif 1745 1746 if (ctx) { 1747 SDL_RAWINPUT_Device *device; 1748 1749 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT 1750 xinput_device_change = SDL_TRUE; 1751 if (ctx->xinput_enabled) { 1752 if (ctx->xinput_correlated) { 1753 RAWINPUT_MarkXInputSlotFree(ctx->xinput_slot); 1754 } 1755 WIN_UnloadXInputDLL(); 1756 } 1757 #endif 1758 #ifdef SDL_JOYSTICK_RAWINPUT_WGI 1759 RAWINPUT_QuitWindowsGamingInput(ctx); 1760 #endif 1761 1762 device = ctx->device; 1763 if (device) { 1764 SDL_assert(device->joystick == joystick); 1765 device->joystick = NULL; 1766 RAWINPUT_ReleaseDevice(device); 1767 } 1768 1769 SDL_free(ctx->data); 1770 SDL_free(ctx->button_indices); 1771 SDL_free(ctx->axis_indices); 1772 SDL_free(ctx->hat_indices); 1773 SDL_free(ctx); 1774 joystick->hwdata = NULL; 1775 } 1776 } 1777 1778 SDL_bool 1779 RAWINPUT_RegisterNotifications(HWND hWnd) 1780 { 1781 RAWINPUTDEVICE rid[SDL_arraysize(subscribed_devices)]; 1782 int i; 1783 1784 for (i = 0; i < SDL_arraysize(subscribed_devices); i++) { 1785 rid[i].usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP; 1786 rid[i].usUsage = subscribed_devices[i]; 1787 rid[i].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; /* Receive messages when in background, including device add/remove */ 1788 rid[i].hwndTarget = hWnd; 1789 } 1790 1791 if (!RegisterRawInputDevices(rid, SDL_arraysize(rid), sizeof(RAWINPUTDEVICE))) { 1792 SDL_SetError("Couldn't register for raw input events"); 1793 return SDL_FALSE; 1794 } 1795 return SDL_TRUE; 1796 } 1797 1798 void 1799 RAWINPUT_UnregisterNotifications() 1800 { 1801 int i; 1802 RAWINPUTDEVICE rid[SDL_arraysize(subscribed_devices)]; 1803 1804 for (i = 0; i < SDL_arraysize(subscribed_devices); i++) { 1805 rid[i].usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP; 1806 rid[i].usUsage = subscribed_devices[i]; 1807 rid[i].dwFlags = RIDEV_REMOVE; 1808 rid[i].hwndTarget = NULL; 1809 } 1810 1811 if (!RegisterRawInputDevices(rid, SDL_arraysize(rid), sizeof(RAWINPUTDEVICE))) { 1812 SDL_SetError("Couldn't unregister for raw input events"); 1813 return; 1814 } 1815 } 1816 1817 LRESULT CALLBACK 1818 RAWINPUT_WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 1819 { 1820 LRESULT result = -1; 1821 1822 SDL_LockMutex(SDL_RAWINPUT_mutex); 1823 1824 if (SDL_RAWINPUT_inited) { 1825 switch (msg) { 1826 case WM_INPUT_DEVICE_CHANGE: 1827 { 1828 HANDLE hDevice = (HANDLE)lParam; 1829 switch (wParam) { 1830 case GIDC_ARRIVAL: 1831 RAWINPUT_AddDevice(hDevice); 1832 break; 1833 case GIDC_REMOVAL: 1834 { 1835 SDL_RAWINPUT_Device *device; 1836 device = RAWINPUT_DeviceFromHandle(hDevice); 1837 if (device) { 1838 RAWINPUT_DelDevice(device, SDL_TRUE); 1839 } 1840 break; 1841 } 1842 default: 1843 break; 1844 } 1845 } 1846 result = 0; 1847 break; 1848 1849 case WM_INPUT: 1850 { 1851 Uint8 data[sizeof(RAWINPUTHEADER) + sizeof(RAWHID) + USB_PACKET_LENGTH]; 1852 UINT buffer_size = SDL_arraysize(data); 1853 1854 if ((int)GetRawInputData((HRAWINPUT)lParam, RID_INPUT, data, &buffer_size, sizeof(RAWINPUTHEADER)) > 0) { 1855 PRAWINPUT raw_input = (PRAWINPUT)data; 1856 SDL_RAWINPUT_Device *device = RAWINPUT_DeviceFromHandle(raw_input->header.hDevice); 1857 if (device) { 1858 SDL_Joystick *joystick = device->joystick; 1859 if (joystick) { 1860 RAWINPUT_HandleStatePacket(joystick, raw_input->data.hid.bRawData, raw_input->data.hid.dwSizeHid); 1861 } 1862 } 1863 } 1864 } 1865 result = 0; 1866 break; 1867 } 1868 } 1869 1870 SDL_UnlockMutex(SDL_RAWINPUT_mutex); 1871 1872 if (result >= 0) { 1873 return result; 1874 } 1875 return CallWindowProc(DefWindowProc, hWnd, msg, wParam, lParam); 1876 } 1877 1878 static void 1879 RAWINPUT_JoystickQuit(void) 1880 { 1881 if (!SDL_RAWINPUT_inited) { 1882 return; 1883 } 1884 1885 SDL_LockMutex(SDL_RAWINPUT_mutex); 1886 1887 while (SDL_RAWINPUT_devices) { 1888 RAWINPUT_DelDevice(SDL_RAWINPUT_devices, SDL_FALSE); 1889 } 1890 1891 WIN_UnloadHIDDLL(); 1892 1893 SDL_RAWINPUT_numjoysticks = 0; 1894 1895 SDL_RAWINPUT_inited = SDL_FALSE; 1896 1897 SDL_UnlockMutex(SDL_RAWINPUT_mutex); 1898 SDL_DestroyMutex(SDL_RAWINPUT_mutex); 1899 SDL_RAWINPUT_mutex = NULL; 1900 } 1901 1902 static SDL_bool 1903 RAWINPUT_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) 1904 { 1905 return SDL_FALSE; 1906 } 1907 1908 SDL_JoystickDriver SDL_RAWINPUT_JoystickDriver = 1909 { 1910 RAWINPUT_JoystickInit, 1911 RAWINPUT_JoystickGetCount, 1912 RAWINPUT_JoystickDetect, 1913 RAWINPUT_JoystickGetDeviceName, 1914 RAWINPUT_JoystickGetDevicePlayerIndex, 1915 RAWINPUT_JoystickSetDevicePlayerIndex, 1916 RAWINPUT_JoystickGetDeviceGUID, 1917 RAWINPUT_JoystickGetDeviceInstanceID, 1918 RAWINPUT_JoystickOpen, 1919 RAWINPUT_JoystickRumble, 1920 RAWINPUT_JoystickRumbleTriggers, 1921 RAWINPUT_JoystickHasLED, 1922 RAWINPUT_JoystickSetLED, 1923 RAWINPUT_JoystickSetSensorsEnabled, 1924 RAWINPUT_JoystickUpdate, 1925 RAWINPUT_JoystickClose, 1926 RAWINPUT_JoystickQuit, 1927 RAWINPUT_JoystickGetGamepadMapping 1928 }; 1929 1930 #endif /* SDL_JOYSTICK_RAWINPUT */ 1931 1932 /* vi: set ts=4 sw=4 expandtab: */