Atlas - SDL_gameinputjoystick.cpp

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