SDL_gameinputjoystick.cpp 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "SDL_internal.h"
  19. #ifdef SDL_JOYSTICK_GAMEINPUT
  20. #include "../SDL_sysjoystick.h"
  21. #include "../usb_ids.h"
  22. #include "../../core/windows/SDL_windows.h"
  23. #include "../../core/windows/SDL_gameinput.h"
  24. enum
  25. {
  26. SDL_GAMEPAD_BUTTON_GAMEINPUT_SHARE = 11
  27. };
  28. enum
  29. {
  30. SDL_GAMEINPUT_RAWTYPE_NONE,
  31. SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_GUITAR,
  32. SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_DRUM_KIT,
  33. SDL_GAMEINPUT_RAWTYPE_GUITAR_HERO_LIVE_GUITAR,
  34. SDL_GAMEINPUT_RAWTYPE_LEGACY_ADAPTER,
  35. };
  36. typedef struct GAMEINPUT_InternalDevice
  37. {
  38. IGameInputDevice *device;
  39. char path[(APP_LOCAL_DEVICE_ID_SIZE * 2) + 1];
  40. char *name;
  41. SDL_GUID guid; // generated by SDL
  42. SDL_JoystickID device_instance; // generated by SDL
  43. const GameInputDeviceInfo *info;
  44. Uint16 vendor;
  45. Uint16 product;
  46. int raw_type;
  47. int steam_virtual_gamepad_slot;
  48. bool isAdded;
  49. bool isDeleteRequested;
  50. } GAMEINPUT_InternalDevice;
  51. typedef struct GAMEINPUT_InternalList
  52. {
  53. GAMEINPUT_InternalDevice **devices;
  54. int count;
  55. } GAMEINPUT_InternalList;
  56. typedef struct joystick_hwdata
  57. {
  58. GAMEINPUT_InternalDevice *devref;
  59. bool report_sensors;
  60. GameInputRumbleParams rumbleParams;
  61. GameInputCallbackToken system_button_callback_token;
  62. } GAMEINPUT_InternalJoystickHwdata;
  63. static GAMEINPUT_InternalList g_GameInputList = { NULL };
  64. static IGameInput *g_pGameInput = NULL;
  65. static GameInputCallbackToken g_GameInputCallbackToken = 0;
  66. static Uint64 g_GameInputTimestampOffset;
  67. static bool GAMEINPUT_InternalIsGamepad(const GameInputDeviceInfo *info)
  68. {
  69. if (info->supportedInput & GameInputKindGamepad) {
  70. return true;
  71. }
  72. return false;
  73. }
  74. static Uint8 GAMEINPUT_GetDeviceRawType(const GameInputDeviceInfo *info)
  75. {
  76. #if GAMEINPUT_API_VERSION >= 3
  77. GameInputKind supportedInput = info->supportedInput;
  78. if (supportedInput & GameInputKindRawDeviceReport) {
  79. switch (info->vendorId) {
  80. case USB_VENDOR_MADCATZ:
  81. switch (info->productId) {
  82. case USB_PRODUCT_MADCATZ_XB1_STRATOCASTER_GUITAR:
  83. return SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_GUITAR;
  84. case USB_PRODUCT_MADCATZ_XB1_DRUM_KIT:
  85. return SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_DRUM_KIT;
  86. case USB_PRODUCT_MADCATZ_XB1_LEGACY_ADAPTER:
  87. return SDL_GAMEINPUT_RAWTYPE_LEGACY_ADAPTER;
  88. default:
  89. break;
  90. }
  91. break;
  92. case USB_VENDOR_PDP:
  93. switch (info->productId) {
  94. case USB_PRODUCT_PDP_XB1_JAGUAR_GUITAR:
  95. case USB_PRODUCT_PDP_XB1_RIFFMASTER_GUITAR:
  96. return SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_GUITAR;
  97. case USB_PRODUCT_PDP_XB1_DRUM_KIT:
  98. return SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_DRUM_KIT;
  99. default:
  100. break;
  101. }
  102. break;
  103. case USB_VENDOR_CRKD:
  104. switch (info->productId) {
  105. case USB_PRODUCT_RED_OCTANE_XB1_STAGE_TOUR_GUITAR:
  106. return SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_GUITAR;
  107. default:
  108. break;
  109. }
  110. break;
  111. case USB_VENDOR_RED_OCTANE:
  112. switch (info->productId) {
  113. case USB_PRODUCT_RED_OCTANE_XB1_GUITAR_HERO_LIVE_GUITAR:
  114. return SDL_GAMEINPUT_RAWTYPE_GUITAR_HERO_LIVE_GUITAR;
  115. default:
  116. break;
  117. }
  118. break;
  119. case USB_VENDOR_RED_OCTANE_GAMES:
  120. switch (info->productId) {
  121. case USB_PRODUCT_RED_OCTANE_XB1_STAGE_TOUR_GUITAR:
  122. return SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_GUITAR;
  123. case USB_PRODUCT_RED_OCTANE_XB1_STAGE_TOUR_DRUMS:
  124. return SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_DRUM_KIT;
  125. default:
  126. break;
  127. }
  128. break;
  129. }
  130. }
  131. #endif // GAMEINPUT_API_VERSION >= 3
  132. return SDL_GAMEINPUT_RAWTYPE_NONE;
  133. }
  134. static Uint8 GAMEINPUT_GetDeviceSubtype(const GameInputDeviceInfo *info)
  135. {
  136. GameInputKind supportedInput = info->supportedInput;
  137. Uint8 rawType = GAMEINPUT_GetDeviceRawType(info);
  138. if (rawType == SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_GUITAR || rawType == SDL_GAMEINPUT_RAWTYPE_GUITAR_HERO_LIVE_GUITAR) {
  139. return SDL_JOYSTICK_TYPE_GUITAR;
  140. }
  141. if (rawType == SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_DRUM_KIT) {
  142. return SDL_JOYSTICK_TYPE_DRUM_KIT;
  143. }
  144. if (supportedInput & GameInputKindRacingWheel) {
  145. return SDL_JOYSTICK_TYPE_WHEEL;
  146. }
  147. if (supportedInput & GameInputKindArcadeStick) {
  148. return SDL_JOYSTICK_TYPE_ARCADE_STICK;
  149. }
  150. if (supportedInput & GameInputKindFlightStick) {
  151. return SDL_JOYSTICK_TYPE_FLIGHT_STICK;
  152. }
  153. if (supportedInput & (GameInputKindGamepad | GameInputKindController)) {
  154. return SDL_JOYSTICK_TYPE_GAMEPAD;
  155. }
  156. // Other device subtypes don't have their own GameInputKind enum entries.
  157. return 0;
  158. }
  159. #if GAMEINPUT_API_VERSION >= 1
  160. static int GetSteamVirtualGamepadSlot(const char *device_path)
  161. {
  162. int slot = -1;
  163. // The format for the raw input device path is documented here:
  164. // https://partner.steamgames.com/doc/features/steam_controller/steam_input_gamepad_emulation_bestpractices
  165. (void)SDL_sscanf(device_path, "\\\\.\\pipe\\HID#VID_045E&PID_028E&IG_00#%*X&%*X&%*X#%d#%*u", &slot);
  166. return slot;
  167. }
  168. #endif // GAMEINPUT_API_VERSION >= 1
  169. static bool IsXbox360WirelessAdapter(Uint16 vendor, Uint16 product)
  170. {
  171. if (vendor == USB_VENDOR_MICROSOFT) {
  172. if (product == USB_PRODUCT_XBOX360_WIRELESS_RECEIVER ||
  173. product == USB_PRODUCT_XBOX360_WIRELESS_RECEIVER_THIRDPARTY1 ||
  174. product == USB_PRODUCT_XBOX360_WIRELESS_RECEIVER_THIRDPARTY2) {
  175. return true;
  176. }
  177. }
  178. return false;
  179. }
  180. static bool GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice)
  181. {
  182. GAMEINPUT_InternalDevice **devicelist = NULL;
  183. GAMEINPUT_InternalDevice *elem = NULL;
  184. const GameInputDeviceInfo *info = NULL;
  185. Uint16 bus = SDL_HARDWARE_BUS_USB;
  186. Uint16 vendor = 0;
  187. Uint16 product = 0;
  188. Uint16 version = 0;
  189. const char *product_string = NULL;
  190. Uint8 driver_signature = 'g';
  191. Uint8 subtype = 0;
  192. int raw_type = SDL_GAMEINPUT_RAWTYPE_NONE;
  193. char tmp[4];
  194. int idx = 0;
  195. SDL_AssertJoysticksLocked();
  196. #if GAMEINPUT_API_VERSION >= 1
  197. HRESULT hr = pDevice->GetDeviceInfo(&info);
  198. if (FAILED(hr)) {
  199. return WIN_SetErrorFromHRESULT("IGameInputDevice::GetDeviceInfo", hr);
  200. }
  201. #else
  202. info = pDevice->GetDeviceInfo();
  203. #endif
  204. if (false /*info->capabilities & GameInputDeviceCapabilityWireless*/) {
  205. bus = SDL_HARDWARE_BUS_BLUETOOTH;
  206. } else {
  207. bus = SDL_HARDWARE_BUS_USB;
  208. }
  209. vendor = info->vendorId;
  210. product = info->productId;
  211. //version = (info->firmwareVersion.major << 8) | info->firmwareVersion.minor;
  212. subtype = GAMEINPUT_GetDeviceSubtype(info);
  213. raw_type = GAMEINPUT_GetDeviceRawType(info);
  214. #if GAMEINPUT_API_VERSION >= 1
  215. if (info->displayName) {
  216. product_string = info->displayName;
  217. }
  218. #else
  219. if (info->displayName) {
  220. product_string = info->displayName->data;
  221. }
  222. #endif
  223. if (IsXbox360WirelessAdapter(vendor, product) ||
  224. SDL_ShouldIgnoreJoystick(vendor, product, version, product_string) ||
  225. SDL_JoystickHandledByAnotherDriver(&SDL_GAMEINPUT_JoystickDriver, vendor, product, version, product_string)) {
  226. return true;
  227. }
  228. #if defined(SDL_JOYSTICK_DINPUT) && defined(SDL_HAPTIC_DINPUT)
  229. // This joystick backend currently doesn't provide a haptic backend, so fallback to DirectInput for haptic-capable devices.
  230. if (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_DIRECTINPUT, true) && info->forceFeedbackMotorCount > 0 && pDevice->IsForceFeedbackMotorPoweredOn(0)) {
  231. return true;
  232. }
  233. #endif
  234. if (!GAMEINPUT_InternalIsGamepad(info) && raw_type == SDL_GAMEINPUT_RAWTYPE_NONE) {
  235. #if defined(SDL_JOYSTICK_DINPUT)
  236. // Let other backends handle non-gamepad controllers to possibly avoid bugs and/or regressions.
  237. if (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_DIRECTINPUT, true)) {
  238. return true;
  239. }
  240. #endif
  241. if (info->supportedInput & GameInputKindController) {
  242. // Maintain GUID compatibility with DirectInput controller mappings.
  243. driver_signature = 0;
  244. subtype = 0;
  245. } else {
  246. // This joystick backend currently doesn't provide proper reading of other joystick types.
  247. return true;
  248. }
  249. }
  250. for (idx = 0; idx < g_GameInputList.count; ++idx) {
  251. elem = g_GameInputList.devices[idx];
  252. if (elem && elem->device == pDevice) {
  253. // we're already added
  254. elem->isDeleteRequested = false;
  255. return true;
  256. }
  257. }
  258. elem = (GAMEINPUT_InternalDevice *)SDL_calloc(1, sizeof(*elem));
  259. if (!elem) {
  260. return false;
  261. }
  262. devicelist = (GAMEINPUT_InternalDevice **)SDL_realloc(g_GameInputList.devices, sizeof(elem) * (g_GameInputList.count + 1LL));
  263. if (!devicelist) {
  264. SDL_free(elem);
  265. return false;
  266. }
  267. // Generate a device path
  268. for (idx = 0; idx < APP_LOCAL_DEVICE_ID_SIZE; ++idx) {
  269. SDL_snprintf(tmp, SDL_arraysize(tmp), "%02X", info->deviceId.value[idx]);
  270. SDL_strlcat(elem->path, tmp, SDL_arraysize(elem->path));
  271. }
  272. pDevice->AddRef();
  273. elem->device = pDevice;
  274. elem->name = SDL_CreateJoystickName(vendor, product, NULL, product_string);
  275. elem->guid = SDL_CreateJoystickGUID(bus, vendor, product, version, NULL, product_string, driver_signature, subtype);
  276. elem->device_instance = SDL_GetNextObjectID();
  277. elem->info = info;
  278. elem->vendor = vendor;
  279. elem->product = product;
  280. elem->raw_type = raw_type;
  281. #if GAMEINPUT_API_VERSION >= 1
  282. elem->steam_virtual_gamepad_slot = GetSteamVirtualGamepadSlot(info->pnpPath);
  283. #else
  284. elem->steam_virtual_gamepad_slot = -1;
  285. #endif
  286. g_GameInputList.devices = devicelist;
  287. g_GameInputList.devices[g_GameInputList.count++] = elem;
  288. return true;
  289. }
  290. static bool GAMEINPUT_InternalRemoveByIndex(int idx)
  291. {
  292. GAMEINPUT_InternalDevice **devicelist = NULL;
  293. GAMEINPUT_InternalDevice *elem;
  294. int bytes = 0;
  295. SDL_AssertJoysticksLocked();
  296. if (idx < 0 || idx >= g_GameInputList.count) {
  297. return SDL_SetError("GAMEINPUT_InternalRemoveByIndex argument idx %d is out of range", idx);
  298. }
  299. elem = g_GameInputList.devices[idx];
  300. if (elem) {
  301. elem->device->Release();
  302. SDL_free(elem->name);
  303. SDL_free(elem);
  304. }
  305. g_GameInputList.devices[idx] = NULL;
  306. if (g_GameInputList.count == 1) {
  307. // last element in the list, free the entire list then
  308. SDL_free(g_GameInputList.devices);
  309. g_GameInputList.devices = NULL;
  310. } else {
  311. if (idx != g_GameInputList.count - 1) {
  312. bytes = sizeof(*devicelist) * (g_GameInputList.count - idx);
  313. SDL_memmove(&g_GameInputList.devices[idx], &g_GameInputList.devices[idx + 1], bytes);
  314. }
  315. }
  316. // decrement the count and return
  317. --g_GameInputList.count;
  318. return true;
  319. }
  320. static GAMEINPUT_InternalDevice *GAMEINPUT_InternalFindByIndex(int idx)
  321. {
  322. // We're guaranteed that the index is in range when this is called
  323. SDL_AssertJoysticksLocked();
  324. return g_GameInputList.devices[idx];
  325. }
  326. static void CALLBACK GAMEINPUT_InternalJoystickDeviceCallback(
  327. _In_ GameInputCallbackToken callbackToken,
  328. _In_ void *context,
  329. _In_ IGameInputDevice *device,
  330. _In_ uint64_t timestamp,
  331. _In_ GameInputDeviceStatus currentStatus,
  332. _In_ GameInputDeviceStatus previousStatus)
  333. {
  334. int idx = 0;
  335. GAMEINPUT_InternalDevice *elem = NULL;
  336. if (!device) {
  337. // This should never happen, but ignore it if it does
  338. return;
  339. }
  340. SDL_LockJoysticks();
  341. if (currentStatus & GameInputDeviceConnected) {
  342. GAMEINPUT_InternalAddOrFind(device);
  343. } else {
  344. for (idx = 0; idx < g_GameInputList.count; ++idx) {
  345. elem = g_GameInputList.devices[idx];
  346. if (elem && elem->device == device) {
  347. // will be deleted on the next Detect call
  348. elem->isDeleteRequested = true;
  349. break;
  350. }
  351. }
  352. }
  353. SDL_UnlockJoysticks();
  354. }
  355. static void GAMEINPUT_JoystickDetect(void);
  356. static void GAMEINPUT_JoystickQuit(void);
  357. static bool GAMEINPUT_IsRawGameInputEnabled(void)
  358. {
  359. #if GAMEINPUT_API_VERSION >= 3
  360. return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_GAMEINPUT_RAW, true);
  361. #else
  362. return false;
  363. #endif
  364. }
  365. static bool GAMEINPUT_JoystickInit(void)
  366. {
  367. HRESULT hr;
  368. if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_GAMEINPUT, SDL_GAMEINPUT_DEFAULT) && !GAMEINPUT_IsRawGameInputEnabled()) {
  369. return true;
  370. }
  371. if (!SDL_InitGameInput(&g_pGameInput)) {
  372. return false;
  373. }
  374. #if GAMEINPUT_API_VERSION >= 2
  375. // Allow background controller input
  376. // SDL manages focus policy at a higher level, so we can set this unconditionally.
  377. g_pGameInput->SetFocusPolicy(GameInputEnableBackgroundInput | GameInputEnableBackgroundGuideButton | GameInputEnableBackgroundShareButton);
  378. #endif
  379. GameInputKind kind = GameInputKindUnknown;
  380. if (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_GAMEINPUT, SDL_GAMEINPUT_DEFAULT)) {
  381. kind |= GameInputKindController;
  382. }
  383. #if GAMEINPUT_API_VERSION >= 3
  384. if (GAMEINPUT_IsRawGameInputEnabled()) {
  385. kind |= GameInputKindRawDeviceReport;
  386. }
  387. #endif
  388. hr = g_pGameInput->RegisterDeviceCallback(NULL,
  389. kind,
  390. GameInputDeviceConnected,
  391. GameInputBlockingEnumeration,
  392. NULL,
  393. GAMEINPUT_InternalJoystickDeviceCallback,
  394. &g_GameInputCallbackToken);
  395. if (FAILED(hr)) {
  396. GAMEINPUT_JoystickQuit();
  397. return WIN_SetErrorFromHRESULT("IGameInput::RegisterDeviceCallback", hr);
  398. }
  399. // Calculate the relative offset between SDL timestamps and GameInput timestamps
  400. Uint64 now = SDL_GetTicksNS();
  401. uint64_t timestampUS = g_pGameInput->GetCurrentTimestamp();
  402. g_GameInputTimestampOffset = (SDL_NS_TO_US(now) - timestampUS);
  403. GAMEINPUT_JoystickDetect();
  404. return true;
  405. }
  406. static int GAMEINPUT_JoystickGetCount(void)
  407. {
  408. SDL_AssertJoysticksLocked();
  409. return g_GameInputList.count;
  410. }
  411. static void GAMEINPUT_JoystickDetect(void)
  412. {
  413. int idx;
  414. GAMEINPUT_InternalDevice *elem = NULL;
  415. SDL_AssertJoysticksLocked();
  416. for (idx = 0; idx < g_GameInputList.count; ++idx) {
  417. elem = g_GameInputList.devices[idx];
  418. if (!elem) {
  419. continue;
  420. }
  421. if (!elem->isAdded) {
  422. SDL_PrivateJoystickAdded(elem->device_instance);
  423. elem->isAdded = true;
  424. }
  425. if (elem->isDeleteRequested || !(elem->device->GetDeviceStatus() & GameInputDeviceConnected)) {
  426. SDL_PrivateJoystickRemoved(elem->device_instance);
  427. GAMEINPUT_InternalRemoveByIndex(idx--);
  428. }
  429. }
  430. }
  431. static bool GAMEINPUT_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
  432. {
  433. SDL_AssertJoysticksLocked();
  434. if (g_pGameInput) {
  435. if (vendor_id == USB_VENDOR_MICROSOFT &&
  436. product_id == USB_PRODUCT_XBOX_ONE_XBOXGIP_CONTROLLER) {
  437. // The Xbox One controller shows up as a hardcoded raw input VID/PID, which we definitely handle
  438. if (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_GAMEINPUT, SDL_GAMEINPUT_DEFAULT)) {
  439. return true;
  440. }
  441. }
  442. for (int i = 0; i < g_GameInputList.count; ++i) {
  443. GAMEINPUT_InternalDevice *elem = g_GameInputList.devices[i];
  444. if (elem && vendor_id == elem->info->vendorId && product_id == elem->info->productId) {
  445. return true;
  446. }
  447. }
  448. }
  449. return false;
  450. }
  451. static const char *GAMEINPUT_JoystickGetDeviceName(int device_index)
  452. {
  453. return GAMEINPUT_InternalFindByIndex(device_index)->name;
  454. }
  455. static const char *GAMEINPUT_JoystickGetDevicePath(int device_index)
  456. {
  457. // APP_LOCAL_DEVICE_ID as a hex string, since it's required for some association callbacks
  458. return GAMEINPUT_InternalFindByIndex(device_index)->path;
  459. }
  460. static int GAMEINPUT_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
  461. {
  462. return GAMEINPUT_InternalFindByIndex(device_index)->steam_virtual_gamepad_slot;
  463. }
  464. static int GAMEINPUT_JoystickGetDevicePlayerIndex(int device_index)
  465. {
  466. return -1;
  467. }
  468. static void GAMEINPUT_JoystickSetDevicePlayerIndex(int device_index, int player_index)
  469. {
  470. }
  471. static SDL_GUID GAMEINPUT_JoystickGetDeviceGUID(int device_index)
  472. {
  473. return GAMEINPUT_InternalFindByIndex(device_index)->guid;
  474. }
  475. static SDL_JoystickID GAMEINPUT_JoystickGetDeviceInstanceID(int device_index)
  476. {
  477. return GAMEINPUT_InternalFindByIndex(device_index)->device_instance;
  478. }
  479. static void GAMEINPUT_UpdatePowerInfo(SDL_Joystick *joystick, IGameInputDevice *device)
  480. {
  481. #if 0
  482. GameInputBatteryState battery_state;
  483. SDL_PowerState state;
  484. int percent = 0;
  485. SDL_zero(battery_state);
  486. IGameInputDevice_GetBatteryState(device, &battery_state);
  487. switch (battery_state.status) {
  488. case GameInputBatteryNotPresent:
  489. state = SDL_POWERSTATE_NO_BATTERY;
  490. break;
  491. case GameInputBatteryDischarging:
  492. state = SDL_POWERSTATE_ON_BATTERY;
  493. break;
  494. case GameInputBatteryIdle:
  495. state = SDL_POWERSTATE_CHARGED;
  496. break;
  497. case GameInputBatteryCharging:
  498. state = SDL_POWERSTATE_CHARGING;
  499. break;
  500. default:
  501. state = SDL_POWERSTATE_UNKNOWN;
  502. break;
  503. }
  504. if (battery_state.fullChargeCapacity > 0.0f) {
  505. percent = (int)SDL_roundf((battery_state.remainingCapacity / battery_state.fullChargeCapacity) * 100.0f);
  506. }
  507. SDL_SendJoystickPowerInfo(joystick, state, percent);
  508. #endif
  509. }
  510. #if GAMEINPUT_API_VERSION >= 1
  511. static void CALLBACK GAMEINPUT_InternalSystemButtonCallback(
  512. _In_ GameInputCallbackToken callbackToken,
  513. _In_ void * context,
  514. _In_ IGameInputDevice * device,
  515. _In_ uint64_t timestampUS,
  516. _In_ GameInputSystemButtons currentButtons,
  517. _In_ GameInputSystemButtons previousButtons)
  518. {
  519. SDL_Joystick *joystick = (SDL_Joystick *)context;
  520. GameInputSystemButtons changedButtons = (previousButtons ^ currentButtons);
  521. if (changedButtons) {
  522. Uint64 timestamp = SDL_US_TO_NS(timestampUS + g_GameInputTimestampOffset);
  523. SDL_LockJoysticks();
  524. if (changedButtons & GameInputSystemButtonGuide) {
  525. bool down = ((currentButtons & GameInputSystemButtonGuide) != 0);
  526. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, down);
  527. }
  528. if (changedButtons & GameInputSystemButtonShare) {
  529. bool down = ((currentButtons & GameInputSystemButtonShare) != 0);
  530. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GAMEINPUT_SHARE, down);
  531. }
  532. SDL_UnlockJoysticks();
  533. }
  534. }
  535. #endif // GAMEINPUT_API_VERSION >= 1
  536. static bool GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index)
  537. {
  538. GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index);
  539. const GameInputDeviceInfo *info = elem->info;
  540. GAMEINPUT_InternalJoystickHwdata *hwdata = NULL;
  541. if (!elem) {
  542. return false;
  543. }
  544. hwdata = (GAMEINPUT_InternalJoystickHwdata *)SDL_calloc(1, sizeof(*hwdata));
  545. if (!hwdata) {
  546. return false;
  547. }
  548. hwdata->devref = elem;
  549. joystick->hwdata = hwdata;
  550. if (GAMEINPUT_InternalIsGamepad(info) || GAMEINPUT_GetDeviceRawType(info) != SDL_GAMEINPUT_RAWTYPE_NONE) {
  551. joystick->naxes = 6;
  552. joystick->nbuttons = 11;
  553. joystick->nhats = 1;
  554. #if GAMEINPUT_API_VERSION >= 1
  555. if (info->supportedSystemButtons != GameInputSystemButtonNone) {
  556. GameInputSystemButtons buttons = GameInputSystemButtonGuide;
  557. if (SDL_IsJoystickXboxSeriesX(elem->vendor, elem->product) &&
  558. (info->supportedSystemButtons & GameInputSystemButtonShare)) {
  559. ++joystick->nbuttons;
  560. buttons |= GameInputSystemButtonShare;
  561. }
  562. g_pGameInput->RegisterSystemButtonCallback(elem->device, buttons, joystick, GAMEINPUT_InternalSystemButtonCallback, &hwdata->system_button_callback_token);
  563. }
  564. #endif // GAMEINPUT_API_VERSION >= 1
  565. } else {
  566. #if GAMEINPUT_API_VERSION >= 3
  567. joystick->naxes = info->controllerInfo->controllerAxisCount;
  568. joystick->nbuttons = info->controllerInfo->controllerButtonCount;
  569. joystick->nhats = info->controllerInfo->controllerSwitchCount;
  570. #else
  571. joystick->naxes = info->controllerAxisCount;
  572. joystick->nbuttons = info->controllerButtonCount;
  573. joystick->nhats = info->controllerSwitchCount;
  574. #endif // GAMEINPUT_API_VERSION >= 3
  575. }
  576. if (info->supportedRumbleMotors & (GameInputRumbleLowFrequency | GameInputRumbleHighFrequency)) {
  577. SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true);
  578. }
  579. if (info->supportedRumbleMotors & (GameInputRumbleLeftTrigger | GameInputRumbleRightTrigger)) {
  580. SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, true);
  581. }
  582. #if GAMEINPUT_API_VERSION >= 3
  583. if (info->supportedInput & GameInputKindSensors) {
  584. if (info->sensorsInfo->supportedSensors & GameInputSensorsGyrometer) {
  585. SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 60.0f);
  586. }
  587. if (info->sensorsInfo->supportedSensors & GameInputSensorsAccelerometer) {
  588. SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 60.0f);
  589. }
  590. }
  591. #endif // GAMEINPUT_API_VERSION >= 3
  592. return true;
  593. }
  594. static bool GAMEINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
  595. {
  596. // don't check for caps here, since SetRumbleState doesn't return any result - we don't need to check it
  597. GAMEINPUT_InternalJoystickHwdata *hwdata = joystick->hwdata;
  598. GameInputRumbleParams *params = &hwdata->rumbleParams;
  599. params->lowFrequency = (float)low_frequency_rumble / (float)SDL_MAX_UINT16;
  600. params->highFrequency = (float)high_frequency_rumble / (float)SDL_MAX_UINT16;
  601. hwdata->devref->device->SetRumbleState(params);
  602. return true;
  603. }
  604. static bool GAMEINPUT_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
  605. {
  606. // don't check for caps here, since SetRumbleState doesn't return any result - we don't need to check it
  607. GAMEINPUT_InternalJoystickHwdata *hwdata = joystick->hwdata;
  608. GameInputRumbleParams *params = &hwdata->rumbleParams;
  609. params->leftTrigger = (float)left_rumble / (float)SDL_MAX_UINT16;
  610. params->rightTrigger = (float)right_rumble / (float)SDL_MAX_UINT16;
  611. hwdata->devref->device->SetRumbleState(params);
  612. return true;
  613. }
  614. static bool GAMEINPUT_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
  615. {
  616. return SDL_Unsupported();
  617. }
  618. static bool GAMEINPUT_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)
  619. {
  620. return SDL_Unsupported();
  621. }
  622. static bool GAMEINPUT_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled)
  623. {
  624. joystick->hwdata->report_sensors = enabled;
  625. return true;
  626. }
  627. static void GAMEINPUT_GuitarUpdate(SDL_Joystick *joystick, IGameInputReading *reading, Uint64 timestamp)
  628. {
  629. #if GAMEINPUT_API_VERSION >= 3
  630. IGameInputRawDeviceReport *rawState;
  631. if (reading->GetRawReport(&rawState)) {
  632. static WORD s_GuitarButtons[] = {
  633. 0x0010, // SDL_GAMEPAD_BUTTON_SOUTH
  634. 0x0020, // SDL_GAMEPAD_BUTTON_EAST
  635. 0x0040, // SDL_GAMEPAD_BUTTON_WEST
  636. 0x0080, // SDL_GAMEPAD_BUTTON_NORTH
  637. 0x0008, // SDL_GAMEPAD_BUTTON_BACK
  638. 0, // The guide button is not available
  639. 0x0004, // SDL_GAMEPAD_BUTTON_START
  640. 0, // right joystick click unavailable
  641. 0x4000, // SDL_GAMEPAD_BUTTON_RIGHT_STICK
  642. 0x1000, // SDL_GAMEPAD_BUTTON_LEFT_SHOULDER
  643. 0, // right shoulder unavailable
  644. };
  645. Uint8 btnidx = 0, hat = 0;
  646. uint8_t rawData[40];
  647. SDL_memset(rawData, 0, sizeof(rawData));
  648. size_t len = rawState->GetRawData(sizeof(rawData), rawData);
  649. uint16_t buttons = rawData[0] | rawData[1] << 8;
  650. if (len >= 10) {
  651. for (btnidx = 0; btnidx < SDL_arraysize(s_GuitarButtons); ++btnidx) {
  652. WORD button_mask = s_GuitarButtons[btnidx];
  653. if (!button_mask) {
  654. continue;
  655. }
  656. bool down = ((buttons & button_mask) != 0);
  657. SDL_SendJoystickButton(timestamp, joystick, btnidx, down);
  658. }
  659. if (buttons & 0x0100) {
  660. hat |= SDL_HAT_UP;
  661. }
  662. if (buttons & 0x0200) {
  663. hat |= SDL_HAT_DOWN;
  664. }
  665. if (buttons & 0x0400) {
  666. hat |= SDL_HAT_LEFT;
  667. }
  668. if (buttons & 0x0800) {
  669. hat |= SDL_HAT_RIGHT;
  670. }
  671. SDL_SendJoystickHat(timestamp, joystick, 0, hat);
  672. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, (rawData[3] * 257) - 32768);
  673. // PS3 RB guitars had tilt on right shoulder
  674. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, rawData[2] >= 0xD0);
  675. // PS3 RB guitars send L2 when using solo buttons
  676. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, (rawData[6]) ? 32767 : -32768);
  677. // Align pickup selector mappings with PS3 instruments
  678. static const Sint16 effects_mappings[] = {-26880, -13568, -1792, 11008, 24576};
  679. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, effects_mappings[rawData[4] >> 4]);
  680. }
  681. }
  682. #endif // GAMEINPUT_API_VERSION >= 3
  683. }
  684. static void GAMEINPUT_GamepadUpdate(SDL_Joystick *joystick, IGameInputReading *reading, Uint64 timestamp) {
  685. GameInputGamepadState state;
  686. static WORD s_XInputButtons[] = {
  687. GameInputGamepadA, // SDL_GAMEPAD_BUTTON_SOUTH
  688. GameInputGamepadB, // SDL_GAMEPAD_BUTTON_EAST
  689. GameInputGamepadX, // SDL_GAMEPAD_BUTTON_WEST
  690. GameInputGamepadY, // SDL_GAMEPAD_BUTTON_NORTH
  691. GameInputGamepadView, // SDL_GAMEPAD_BUTTON_BACK
  692. 0, // The guide button is not available
  693. GameInputGamepadMenu, // SDL_GAMEPAD_BUTTON_START
  694. GameInputGamepadLeftThumbstick, // SDL_GAMEPAD_BUTTON_LEFT_STICK
  695. GameInputGamepadRightThumbstick, // SDL_GAMEPAD_BUTTON_RIGHT_STICK
  696. GameInputGamepadLeftShoulder, // SDL_GAMEPAD_BUTTON_LEFT_SHOULDER
  697. GameInputGamepadRightShoulder, // SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER
  698. };
  699. Uint8 btnidx = 0, hat = 0;
  700. if (reading->GetGamepadState(&state)) {
  701. for (btnidx = 0; btnidx < SDL_arraysize(s_XInputButtons); ++btnidx) {
  702. WORD button_mask = s_XInputButtons[btnidx];
  703. if (!button_mask) {
  704. continue;
  705. }
  706. bool down = ((state.buttons & button_mask) != 0);
  707. SDL_SendJoystickButton(timestamp, joystick, btnidx, down);
  708. }
  709. if (state.buttons & GameInputGamepadDPadUp) {
  710. hat |= SDL_HAT_UP;
  711. }
  712. if (state.buttons & GameInputGamepadDPadDown) {
  713. hat |= SDL_HAT_DOWN;
  714. }
  715. if (state.buttons & GameInputGamepadDPadLeft) {
  716. hat |= SDL_HAT_LEFT;
  717. }
  718. if (state.buttons & GameInputGamepadDPadRight) {
  719. hat |= SDL_HAT_RIGHT;
  720. }
  721. SDL_SendJoystickHat(timestamp, joystick, 0, hat);
  722. #define CONVERT_AXIS(v) (Sint16)(((v) < 0.0f) ? ((v)*32768.0f) : ((v)*32767.0f))
  723. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, CONVERT_AXIS(state.leftThumbstickX));
  724. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, CONVERT_AXIS(-state.leftThumbstickY));
  725. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, CONVERT_AXIS(state.rightThumbstickX));
  726. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, CONVERT_AXIS(-state.rightThumbstickY));
  727. #undef CONVERT_AXIS
  728. #define CONVERT_TRIGGER(v) (Sint16)((v)*65535.0f - 32768.0f)
  729. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, CONVERT_TRIGGER(state.leftTrigger));
  730. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, CONVERT_TRIGGER(state.rightTrigger));
  731. #undef CONVERT_TRIGGER
  732. }
  733. }
  734. static void GAMEINPUT_ControllerUpdate(SDL_Joystick *joystick, IGameInputReading *reading, Uint64 timestamp)
  735. {
  736. bool *button_state = SDL_stack_alloc(bool, joystick->nbuttons);
  737. float *axis_state = SDL_stack_alloc(float, joystick->naxes);
  738. GameInputSwitchPosition *switch_state = SDL_stack_alloc(GameInputSwitchPosition, joystick->nhats);
  739. if (button_state) {
  740. uint32_t i;
  741. uint32_t button_count = reading->GetControllerButtonState(joystick->nbuttons, button_state);
  742. for (i = 0; i < button_count; ++i) {
  743. SDL_SendJoystickButton(timestamp, joystick, (Uint8)i, button_state[i]);
  744. }
  745. SDL_stack_free(button_state);
  746. }
  747. #define CONVERT_AXIS(v) (Sint16)((v)*65535.0f - 32768.0f)
  748. if (axis_state) {
  749. uint32_t i;
  750. uint32_t axis_count = reading->GetControllerAxisState(joystick->naxes, axis_state);
  751. for (i = 0; i < axis_count; ++i) {
  752. SDL_SendJoystickAxis(timestamp, joystick, (Uint8)i, CONVERT_AXIS(axis_state[i]));
  753. }
  754. SDL_stack_free(axis_state);
  755. }
  756. #undef CONVERT_AXIS
  757. if (switch_state) {
  758. uint32_t i;
  759. uint32_t switch_count = reading->GetControllerSwitchState(joystick->nhats, switch_state);
  760. for (i = 0; i < switch_count; ++i) {
  761. Uint8 hat;
  762. switch (switch_state[i]) {
  763. case GameInputSwitchUp:
  764. hat = SDL_HAT_UP;
  765. break;
  766. case GameInputSwitchUpRight:
  767. hat = SDL_HAT_UP | SDL_HAT_RIGHT;
  768. break;
  769. case GameInputSwitchRight:
  770. hat = SDL_HAT_RIGHT;
  771. break;
  772. case GameInputSwitchDownRight:
  773. hat = SDL_HAT_DOWN | SDL_HAT_RIGHT;
  774. break;
  775. case GameInputSwitchDown:
  776. hat = SDL_HAT_DOWN;
  777. break;
  778. case GameInputSwitchDownLeft:
  779. hat = SDL_HAT_DOWN | SDL_HAT_LEFT;
  780. break;
  781. case GameInputSwitchLeft:
  782. hat = SDL_HAT_LEFT;
  783. break;
  784. case GameInputSwitchUpLeft:
  785. hat = SDL_HAT_UP | SDL_HAT_LEFT;
  786. break;
  787. case GameInputSwitchCenter:
  788. default:
  789. hat = SDL_HAT_CENTERED;
  790. break;
  791. }
  792. SDL_SendJoystickHat(timestamp, joystick, (Uint8)i, hat);
  793. }
  794. SDL_stack_free(switch_state);
  795. }
  796. }
  797. static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick)
  798. {
  799. GAMEINPUT_InternalJoystickHwdata *hwdata = joystick->hwdata;
  800. GAMEINPUT_InternalDevice *internal_device = hwdata->devref;
  801. IGameInputDevice *device = hwdata->devref->device;
  802. const GameInputDeviceInfo *info = hwdata->devref->info;
  803. IGameInputReading *reading = NULL;
  804. Uint64 timestamp;
  805. HRESULT hr;
  806. hr = g_pGameInput->GetCurrentReading(info->supportedInput, device, &reading);
  807. if (FAILED(hr)) {
  808. // don't SetError here since there can be a legitimate case when there's no reading avail
  809. return;
  810. }
  811. timestamp = SDL_US_TO_NS(reading->GetTimestamp() + g_GameInputTimestampOffset);
  812. if (internal_device->raw_type == SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_GUITAR) {
  813. GAMEINPUT_GuitarUpdate(joystick, reading, timestamp);
  814. } else if (GAMEINPUT_InternalIsGamepad(info)) {
  815. GAMEINPUT_GamepadUpdate(joystick, reading, timestamp);
  816. } else {
  817. GAMEINPUT_ControllerUpdate(joystick, reading, timestamp);
  818. }
  819. #if GAMEINPUT_API_VERSION >= 3
  820. if (hwdata->report_sensors) {
  821. GameInputSensorsState sensor_state;
  822. if (reading->GetSensorsState(&sensor_state)) {
  823. if ((info->sensorsInfo->supportedSensors & GameInputSensorsGyrometer) != 0) {
  824. float data[3] = {
  825. sensor_state.angularVelocityInRadPerSecX,
  826. sensor_state.angularVelocityInRadPerSecY,
  827. sensor_state.angularVelocityInRadPerSecZ
  828. };
  829. SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, timestamp, data, SDL_arraysize(data));
  830. }
  831. if ((info->sensorsInfo->supportedSensors & GameInputSensorsAccelerometer) != 0) {
  832. float data[3] = {
  833. sensor_state.accelerationInGX * SDL_STANDARD_GRAVITY,
  834. sensor_state.accelerationInGY * SDL_STANDARD_GRAVITY,
  835. sensor_state.accelerationInGZ * SDL_STANDARD_GRAVITY
  836. };
  837. SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, timestamp, data, SDL_arraysize(data));
  838. }
  839. }
  840. }
  841. #endif // GAMEINPUT_API_VERSION >= 3
  842. reading->Release();
  843. // FIXME: We can poll this at a much lower rate
  844. GAMEINPUT_UpdatePowerInfo(joystick, device);
  845. }
  846. static void GAMEINPUT_JoystickClose(SDL_Joystick *joystick)
  847. {
  848. GAMEINPUT_InternalJoystickHwdata *hwdata = joystick->hwdata;
  849. if (hwdata->system_button_callback_token) {
  850. #if GAMEINPUT_API_VERSION >= 1
  851. g_pGameInput->UnregisterCallback(hwdata->system_button_callback_token);
  852. #else
  853. g_pGameInput->UnregisterCallback(hwdata->system_button_callback_token, 10000);
  854. #endif
  855. }
  856. SDL_free(hwdata);
  857. joystick->hwdata = NULL;
  858. }
  859. static void GAMEINPUT_JoystickQuit(void)
  860. {
  861. if (g_pGameInput) {
  862. // free the callback
  863. if (g_GameInputCallbackToken) {
  864. #if GAMEINPUT_API_VERSION >= 1
  865. g_pGameInput->UnregisterCallback(g_GameInputCallbackToken);
  866. #else
  867. g_pGameInput->UnregisterCallback(g_GameInputCallbackToken, 10000);
  868. #endif
  869. g_GameInputCallbackToken = 0;
  870. }
  871. // free the list
  872. while (g_GameInputList.count > 0) {
  873. GAMEINPUT_InternalRemoveByIndex(0);
  874. }
  875. SDL_QuitGameInput();
  876. g_pGameInput = NULL;
  877. }
  878. }
  879. static bool GAMEINPUT_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
  880. {
  881. GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index);
  882. if (!GAMEINPUT_InternalIsGamepad(elem->info) && elem->raw_type == SDL_GAMEINPUT_RAWTYPE_NONE) {
  883. return false;
  884. }
  885. out->a.kind = EMappingKind_Button;
  886. out->a.target = SDL_GAMEPAD_BUTTON_SOUTH;
  887. out->b.kind = EMappingKind_Button;
  888. out->b.target = SDL_GAMEPAD_BUTTON_EAST;
  889. out->x.kind = EMappingKind_Button;
  890. out->x.target = SDL_GAMEPAD_BUTTON_WEST;
  891. out->y.kind = EMappingKind_Button;
  892. out->y.target = SDL_GAMEPAD_BUTTON_NORTH;
  893. out->back.kind = EMappingKind_Button;
  894. out->back.target = SDL_GAMEPAD_BUTTON_BACK;
  895. #if GAMEINPUT_API_VERSION >= 1
  896. if (elem->info->supportedSystemButtons & GameInputSystemButtonGuide) {
  897. out->guide.kind = EMappingKind_Button;
  898. out->guide.target = SDL_GAMEPAD_BUTTON_GUIDE;
  899. }
  900. if (SDL_IsJoystickXboxSeriesX(elem->vendor, elem->product) &&
  901. (elem->info->supportedSystemButtons & GameInputSystemButtonShare)) {
  902. out->misc1.kind = EMappingKind_Button;
  903. out->misc1.target = SDL_GAMEPAD_BUTTON_GAMEINPUT_SHARE;
  904. }
  905. #endif // GAMEINPUT_API_VERSION >= 1
  906. out->start.kind = EMappingKind_Button;
  907. out->start.target = SDL_GAMEPAD_BUTTON_START;
  908. out->leftstick.kind = EMappingKind_Button;
  909. out->leftstick.target = SDL_GAMEPAD_BUTTON_LEFT_STICK;
  910. out->rightstick.kind = EMappingKind_Button;
  911. out->rightstick.target = SDL_GAMEPAD_BUTTON_RIGHT_STICK;
  912. out->leftshoulder.kind = EMappingKind_Button;
  913. out->leftshoulder.target = SDL_GAMEPAD_BUTTON_LEFT_SHOULDER;
  914. out->rightshoulder.kind = EMappingKind_Button;
  915. out->rightshoulder.target = SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER;
  916. out->dpup.kind = EMappingKind_Hat;
  917. out->dpup.target = SDL_HAT_UP;
  918. out->dpdown.kind = EMappingKind_Hat;
  919. out->dpdown.target = SDL_HAT_DOWN;
  920. out->dpleft.kind = EMappingKind_Hat;
  921. out->dpleft.target = SDL_HAT_LEFT;
  922. out->dpright.kind = EMappingKind_Hat;
  923. out->dpright.target = SDL_HAT_RIGHT;
  924. out->leftx.kind = EMappingKind_Axis;
  925. out->leftx.target = SDL_GAMEPAD_AXIS_LEFTX;
  926. out->lefty.kind = EMappingKind_Axis;
  927. out->lefty.target = SDL_GAMEPAD_AXIS_LEFTY;
  928. out->rightx.kind = EMappingKind_Axis;
  929. out->rightx.target = SDL_GAMEPAD_AXIS_RIGHTX;
  930. out->righty.kind = EMappingKind_Axis;
  931. out->righty.target = SDL_GAMEPAD_AXIS_RIGHTY;
  932. out->lefttrigger.kind = EMappingKind_Axis;
  933. out->lefttrigger.target = SDL_GAMEPAD_AXIS_LEFT_TRIGGER;
  934. out->righttrigger.kind = EMappingKind_Axis;
  935. out->righttrigger.target = SDL_GAMEPAD_AXIS_RIGHT_TRIGGER;
  936. return true;
  937. }
  938. SDL_JoystickDriver SDL_GAMEINPUT_JoystickDriver =
  939. {
  940. GAMEINPUT_JoystickInit,
  941. GAMEINPUT_JoystickGetCount,
  942. GAMEINPUT_JoystickDetect,
  943. GAMEINPUT_JoystickIsDevicePresent,
  944. GAMEINPUT_JoystickGetDeviceName,
  945. GAMEINPUT_JoystickGetDevicePath,
  946. GAMEINPUT_JoystickGetDeviceSteamVirtualGamepadSlot,
  947. GAMEINPUT_JoystickGetDevicePlayerIndex,
  948. GAMEINPUT_JoystickSetDevicePlayerIndex,
  949. GAMEINPUT_JoystickGetDeviceGUID,
  950. GAMEINPUT_JoystickGetDeviceInstanceID,
  951. GAMEINPUT_JoystickOpen,
  952. GAMEINPUT_JoystickRumble,
  953. GAMEINPUT_JoystickRumbleTriggers,
  954. GAMEINPUT_JoystickSetLED,
  955. GAMEINPUT_JoystickSendEffect,
  956. GAMEINPUT_JoystickSetSensorsEnabled,
  957. GAMEINPUT_JoystickUpdate,
  958. GAMEINPUT_JoystickClose,
  959. GAMEINPUT_JoystickQuit,
  960. GAMEINPUT_JoystickGetGamepadMapping
  961. };
  962. #endif // SDL_JOYSTICK_GAMEINPUT