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