Atlas - SDL_hidapi_ps4.c
Home / ext / SDL2 / src / joystick / hidapi Lines: 3 | Size: 18301 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 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/* This driver supports both simplified reports and the extended input reports enabled by Steam. 22 Code and logic contributed by Valve Corporation under the SDL zlib license. 23*/ 24#include "../../SDL_internal.h" 25 26#ifdef SDL_JOYSTICK_HIDAPI 27 28#include "SDL_hints.h" 29#include "SDL_log.h" 30#include "SDL_events.h" 31#include "SDL_timer.h" 32#include "SDL_joystick.h" 33#include "SDL_gamecontroller.h" 34#include "../SDL_sysjoystick.h" 35#include "SDL_hidapijoystick_c.h" 36 37 38#ifdef SDL_JOYSTICK_HIDAPI_PS4 39 40#define SONY_USB_VID 0x054C 41#define SONY_DS4_PID 0x05C4 42#define SONY_DS4_DONGLE_PID 0x0BA0 43#define SONY_DS4_SLIM_PID 0x09CC 44 45#define USB_PACKET_LENGTH 64 46 47#define VOLUME_CHECK_INTERVAL_MS (10 * 1000) 48 49typedef enum 50{ 51 k_EPS4ReportIdUsbState = 1, 52 k_EPS4ReportIdUsbEffects = 5, 53 k_EPS4ReportIdBluetoothState = 17, 54 k_EPS4ReportIdBluetoothEffects = 17, 55 k_EPS4ReportIdDisconnectMessage = 226, 56} EPS4ReportId; 57 58typedef enum 59{ 60 k_ePS4FeatureReportIdGyroCalibration_USB = 0x02, 61 k_ePS4FeatureReportIdGyroCalibration_BT = 0x05, 62 k_ePS4FeatureReportIdSerialNumber = 0x12, 63} EPS4FeatureReportID; 64 65typedef struct 66{ 67 Uint8 ucLeftJoystickX; 68 Uint8 ucLeftJoystickY; 69 Uint8 ucRightJoystickX; 70 Uint8 ucRightJoystickY; 71 Uint8 rgucButtonsHatAndCounter[ 3 ]; 72 Uint8 ucTriggerLeft; 73 Uint8 ucTriggerRight; 74 Uint8 _rgucPad0[ 3 ]; 75 Sint16 sGyroX; 76 Sint16 sGyroY; 77 Sint16 sGyroZ; 78 Sint16 sAccelX; 79 Sint16 sAccelY; 80 Sint16 sAccelZ; 81 Uint8 _rgucPad1[ 5 ]; 82 Uint8 ucBatteryLevel; 83 Uint8 _rgucPad2[ 4 ]; 84 Uint8 ucTrackpadCounter1; 85 Uint8 rgucTrackpadData1[ 3 ]; 86 Uint8 ucTrackpadCounter2; 87 Uint8 rgucTrackpadData2[ 3 ]; 88} PS4StatePacket_t; 89 90typedef struct 91{ 92 Uint8 ucRumbleRight; 93 Uint8 ucRumbleLeft; 94 Uint8 ucLedRed; 95 Uint8 ucLedGreen; 96 Uint8 ucLedBlue; 97 Uint8 ucLedDelayOn; 98 Uint8 ucLedDelayOff; 99 Uint8 _rgucPad0[ 8 ]; 100 Uint8 ucVolumeLeft; 101 Uint8 ucVolumeRight; 102 Uint8 ucVolumeMic; 103 Uint8 ucVolumeSpeaker; 104} DS4EffectsState_t; 105 106typedef struct { 107 SDL_bool is_dongle; 108 SDL_bool is_bluetooth; 109 SDL_bool audio_supported; 110 Uint8 volume; 111 Uint32 last_volume_check; 112 Uint32 rumble_expiration; 113 PS4StatePacket_t last_state; 114} SDL_DriverPS4_Context; 115 116 117/* Public domain CRC implementation adapted from: 118 http://home.thep.lu.se/~bjorn/crc/crc32_simple.c 119*/ 120static Uint32 crc32_for_byte(Uint32 r) 121{ 122 int i; 123 for(i = 0; i < 8; ++i) { 124 r = (r & 1? 0: (Uint32)0xEDB88320L) ^ r >> 1; 125 } 126 return r ^ (Uint32)0xFF000000L; 127} 128 129static Uint32 crc32(Uint32 crc, const void *data, int count) 130{ 131 int i; 132 for(i = 0; i < count; ++i) { 133 crc = crc32_for_byte((Uint8)crc ^ ((const Uint8*)data)[i]) ^ crc >> 8; 134 } 135 return crc; 136} 137 138#ifdef __WIN32__ 139#include "../../core/windows/SDL_windows.h" 140 141#ifndef NTDDI_VISTA 142#define NTDDI_VISTA 0x06000000 143#endif 144#ifndef _WIN32_WINNT_VISTA 145#define _WIN32_WINNT_VISTA 0x0600 146#endif 147 148/* Define Vista for the Audio related includes below to work */ 149#undef NTDDI_VERSION 150#define NTDDI_VERSION NTDDI_VISTA 151#undef _WIN32_WINNT 152#define _WIN32_WINNT _WIN32_WINNT_VISTA 153#define COBJMACROS 154#include <mmdeviceapi.h> 155#include <audioclient.h> 156#include <endpointvolume.h> 157 158#undef DEFINE_GUID 159#define DEFINE_GUID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const GUID n = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} 160DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E); 161DEFINE_GUID(IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6); 162DEFINE_GUID(IID_IAudioEndpointVolume, 0x5CDF2C82, 0x841E, 0x4546, 0x97, 0x22, 0x0C, 0xF7, 0x40, 0x78, 0x22, 0x9A); 163#endif 164 165 166 167static float GetSystemVolume(void) 168{ 169 float volume = -1.0f; /* Return this if we can't get system volume */ 170 171#ifdef __WIN32__ 172 HRESULT hr = WIN_CoInitialize(); 173 if (SUCCEEDED(hr)) { 174 IMMDeviceEnumerator *pEnumerator; 175 176 /* This should gracefully fail on XP and succeed on everything Vista and above */ 177 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (LPVOID*)&pEnumerator); 178 if (SUCCEEDED(hr)) { 179 IMMDevice *pDevice; 180 181 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eRender, eConsole, &pDevice); 182 if (SUCCEEDED(hr)) { 183 IAudioEndpointVolume *pEndpointVolume; 184 185 hr = IMMDevice_Activate(pDevice, &IID_IAudioEndpointVolume, CLSCTX_ALL, NULL, (LPVOID*)&pEndpointVolume); 186 if (SUCCEEDED(hr)) { 187 IAudioEndpointVolume_GetMasterVolumeLevelScalar(pEndpointVolume, &volume); 188 IUnknown_Release(pEndpointVolume); 189 } 190 IUnknown_Release(pDevice); 191 } 192 IUnknown_Release(pEnumerator); 193 } 194 WIN_CoUninitialize(); 195 } 196#endif /* __WIN32__ */ 197 198 return volume; 199} 200 201static uint8_t GetPlaystationVolumeFromFloat(float fVolume) 202{ 203 const int k_nVolumeFitRatio = 15; 204 const int k_nVolumeFitOffset = 9; 205 float fVolLog; 206 207 if (fVolume > 1.0f || fVolume < 0.0f) { 208 fVolume = 0.30f; 209 } 210 fVolLog = SDL_logf(fVolume * 100); 211 212 return (Uint8)((fVolLog * k_nVolumeFitRatio) + k_nVolumeFitOffset); 213} 214 215static SDL_bool 216HIDAPI_DriverPS4_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, Uint16 usage_page, Uint16 usage) 217{ 218 /* The Revolution Pro Controller exposes multiple interfaces on Windows */ 219 const Uint16 NACON_USB_VID = 0x146b; 220 if (vendor_id == NACON_USB_VID && usage_page != 0 && usage_page != 1) { 221 return SDL_FALSE; 222 } 223 224 return SDL_IsJoystickPS4(vendor_id, product_id); 225} 226 227static const char * 228HIDAPI_DriverPS4_GetDeviceName(Uint16 vendor_id, Uint16 product_id) 229{ 230 if (vendor_id == SONY_USB_VID) { 231 return "PS4 Controller"; 232 } 233 return NULL; 234} 235 236static SDL_bool ReadFeatureReport(hid_device *dev, Uint8 report_id, Uint8 *data, size_t size) 237{ 238 Uint8 report[USB_PACKET_LENGTH + 1]; 239 240 SDL_memset(report, 0, sizeof(report)); 241 report[0] = report_id; 242 if (hid_get_feature_report(dev, report, sizeof(report)) < 0) { 243 return SDL_FALSE; 244 } 245 SDL_memcpy(data, report, SDL_min(size, sizeof(report))); 246 return SDL_TRUE; 247} 248 249static SDL_bool CheckUSBConnected(hid_device *dev) 250{ 251 int i; 252 Uint8 data[16]; 253 254 /* This will fail if we're on Bluetooth */ 255 if (ReadFeatureReport(dev, k_ePS4FeatureReportIdSerialNumber, data, sizeof(data))) { 256 for (i = 0; i < sizeof(data); ++i) { 257 if (data[i] != 0x00) { 258 return SDL_TRUE; 259 } 260 } 261 /* Maybe the dongle without a connected controller? */ 262 } 263 return SDL_FALSE; 264} 265 266static int HIDAPI_DriverPS4_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms); 267 268static SDL_bool 269HIDAPI_DriverPS4_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context) 270{ 271 SDL_DriverPS4_Context *ctx; 272 273 ctx = (SDL_DriverPS4_Context *)SDL_calloc(1, sizeof(*ctx)); 274 if (!ctx) { 275 SDL_OutOfMemory(); 276 return SDL_FALSE; 277 } 278 *context = ctx; 279 280 /* Check for type of connection */ 281 ctx->is_dongle = (vendor_id == SONY_USB_VID && product_id == SONY_DS4_DONGLE_PID); 282 if (ctx->is_dongle) { 283 ctx->is_bluetooth = SDL_FALSE; 284 } else if (vendor_id == SONY_USB_VID) { 285 ctx->is_bluetooth = !CheckUSBConnected(dev); 286 } else { 287 /* Third party controllers appear to all be wired */ 288 ctx->is_bluetooth = SDL_FALSE; 289 } 290#ifdef DEBUG_PS4 291 SDL_Log("PS4 dongle = %s, bluetooth = %s\n", ctx->is_dongle ? "TRUE" : "FALSE", ctx->is_bluetooth ? "TRUE" : "FALSE"); 292#endif 293 294 /* Check to see if audio is supported */ 295 if (vendor_id == SONY_USB_VID && 296 (product_id == SONY_DS4_SLIM_PID || product_id == SONY_DS4_DONGLE_PID )) { 297 ctx->audio_supported = SDL_TRUE; 298 } 299 300 /* Initialize LED and effect state */ 301 HIDAPI_DriverPS4_Rumble(joystick, dev, ctx, 0, 0, 0); 302 303 /* Initialize the joystick capabilities */ 304 joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX; 305 joystick->naxes = SDL_CONTROLLER_AXIS_MAX; 306 joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; 307 308 return SDL_TRUE; 309} 310 311static int 312HIDAPI_DriverPS4_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) 313{ 314 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context; 315 DS4EffectsState_t *effects; 316 Uint8 data[78]; 317 int report_size, offset; 318 319 /* In order to send rumble, we have to send a complete effect packet */ 320 SDL_memset(data, 0, sizeof(data)); 321 322 if (ctx->is_bluetooth) { 323 data[0] = k_EPS4ReportIdBluetoothEffects; 324 data[1] = 0xC0 | 0x04; /* Magic value HID + CRC, also sets interval to 4ms for samples */ 325 data[3] = 0x03; /* 0x1 is rumble, 0x2 is lightbar, 0x4 is the blink interval */ 326 327 report_size = 78; 328 offset = 6; 329 } else { 330 data[0] = k_EPS4ReportIdUsbEffects; 331 data[1] = 0x07; /* Magic value */ 332 333 report_size = 32; 334 offset = 4; 335 } 336 effects = (DS4EffectsState_t *)&data[offset]; 337 338 effects->ucRumbleLeft = (low_frequency_rumble >> 8); 339 effects->ucRumbleRight = (high_frequency_rumble >> 8); 340 341 effects->ucLedRed = 0; 342 effects->ucLedGreen = 0; 343 effects->ucLedBlue = 80; 344 345 if (ctx->audio_supported) { 346 Uint32 now = SDL_GetTicks(); 347 if (!ctx->last_volume_check || 348 SDL_TICKS_PASSED(now, ctx->last_volume_check + VOLUME_CHECK_INTERVAL_MS)) { 349 ctx->volume = GetPlaystationVolumeFromFloat(GetSystemVolume()); 350 ctx->last_volume_check = now; 351 } 352 353 effects->ucVolumeRight = ctx->volume; 354 effects->ucVolumeLeft = ctx->volume; 355 effects->ucVolumeSpeaker = ctx->volume; 356 effects->ucVolumeMic = 0xFF; 357 } 358 359 if (ctx->is_bluetooth) { 360 /* Bluetooth reports need a CRC at the end of the packet (at least on Linux) */ 361 Uint8 ubHdr = 0xA2; /* hidp header is part of the CRC calculation */ 362 Uint32 unCRC; 363 unCRC = crc32(0, &ubHdr, 1); 364 unCRC = crc32(unCRC, data, (Uint32)(report_size - sizeof(unCRC))); 365 SDL_memcpy(&data[report_size - sizeof(unCRC)], &unCRC, sizeof(unCRC)); 366 } 367 368 if (hid_write(dev, data, report_size) != report_size) { 369 return SDL_SetError("Couldn't send rumble packet"); 370 } 371 372 if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) { 373 ctx->rumble_expiration = SDL_GetTicks() + duration_ms; 374 } else { 375 ctx->rumble_expiration = 0; 376 } 377 return 0; 378} 379 380static void 381HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS4_Context *ctx, PS4StatePacket_t *packet) 382{ 383 Sint16 axis; 384 385 if (ctx->last_state.rgucButtonsHatAndCounter[0] != packet->rgucButtonsHatAndCounter[0]) { 386 { 387 Uint8 data = (packet->rgucButtonsHatAndCounter[0] >> 4); 388 389 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED); 390 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED); 391 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED); 392 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED); 393 } 394 { 395 Uint8 data = (packet->rgucButtonsHatAndCounter[0] & 0x0F); 396 SDL_bool dpad_up = SDL_FALSE; 397 SDL_bool dpad_down = SDL_FALSE; 398 SDL_bool dpad_left = SDL_FALSE; 399 SDL_bool dpad_right = SDL_FALSE; 400 401 switch (data) { 402 case 0: 403 dpad_up = SDL_TRUE; 404 break; 405 case 1: 406 dpad_up = SDL_TRUE; 407 dpad_right = SDL_TRUE; 408 break; 409 case 2: 410 dpad_right = SDL_TRUE; 411 break; 412 case 3: 413 dpad_right = SDL_TRUE; 414 dpad_down = SDL_TRUE; 415 break; 416 case 4: 417 dpad_down = SDL_TRUE; 418 break; 419 case 5: 420 dpad_left = SDL_TRUE; 421 dpad_down = SDL_TRUE; 422 break; 423 case 6: 424 dpad_left = SDL_TRUE; 425 break; 426 case 7: 427 dpad_up = SDL_TRUE; 428 dpad_left = SDL_TRUE; 429 break; 430 default: 431 break; 432 } 433 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down); 434 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up); 435 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right); 436 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left); 437 } 438 } 439 440 if (ctx->last_state.rgucButtonsHatAndCounter[1] != packet->rgucButtonsHatAndCounter[1]) { 441 Uint8 data = packet->rgucButtonsHatAndCounter[1]; 442 443 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED); 444 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED); 445 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED); 446 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED); 447 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED); 448 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x80) ? SDL_PRESSED : SDL_RELEASED); 449 } 450 451 if (ctx->last_state.rgucButtonsHatAndCounter[2] != packet->rgucButtonsHatAndCounter[2]) { 452 Uint8 data = (packet->rgucButtonsHatAndCounter[2] & 0x03); 453 454 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED); 455 } 456 457 axis = ((int)packet->ucTriggerLeft * 257) - 32768; 458 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis); 459 axis = ((int)packet->ucTriggerRight * 257) - 32768; 460 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis); 461 axis = ((int)packet->ucLeftJoystickX * 257) - 32768; 462 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis); 463 axis = ((int)packet->ucLeftJoystickY * 257) - 32768; 464 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis); 465 axis = ((int)packet->ucRightJoystickX * 257) - 32768; 466 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis); 467 axis = ((int)packet->ucRightJoystickY * 257) - 32768; 468 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis); 469 470 if (packet->ucBatteryLevel & 0x10) { 471 joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; 472 } else { 473 /* Battery level ranges from 0 to 10 */ 474 int level = (packet->ucBatteryLevel & 0xF); 475 if (level == 0) { 476 joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY; 477 } else if (level <= 2) { 478 joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW; 479 } else if (level <= 7) { 480 joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM; 481 } else { 482 joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL; 483 } 484 } 485 486 SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state)); 487} 488 489static SDL_bool 490HIDAPI_DriverPS4_Update(SDL_Joystick *joystick, hid_device *dev, void *context) 491{ 492 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context; 493 Uint8 data[USB_PACKET_LENGTH]; 494 int size; 495 496 while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) { 497 switch (data[0]) { 498 case k_EPS4ReportIdUsbState: 499 HIDAPI_DriverPS4_HandleStatePacket(joystick, dev, ctx, (PS4StatePacket_t *)&data[1]); 500 break; 501 case k_EPS4ReportIdBluetoothState: 502 /* Bluetooth state packets have two additional bytes at the beginning */ 503 HIDAPI_DriverPS4_HandleStatePacket(joystick, dev, ctx, (PS4StatePacket_t *)&data[3]); 504 break; 505 default: 506#ifdef DEBUG_JOYSTICK 507 SDL_Log("Unknown PS4 packet: 0x%.2x\n", data[0]); 508#endif 509 break; 510 } 511 } 512 513 if (ctx->rumble_expiration) { 514 Uint32 now = SDL_GetTicks(); 515 if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) { 516 HIDAPI_DriverPS4_Rumble(joystick, dev, context, 0, 0, 0); 517 } 518 } 519 520 return (size >= 0); 521} 522 523static void 524HIDAPI_DriverPS4_Quit(SDL_Joystick *joystick, hid_device *dev, void *context) 525{ 526 SDL_free(context); 527} 528 529SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4 = 530{ 531 SDL_HINT_JOYSTICK_HIDAPI_PS4, 532 SDL_TRUE, 533 HIDAPI_DriverPS4_IsSupportedDevice, 534 HIDAPI_DriverPS4_GetDeviceName, 535 HIDAPI_DriverPS4_Init, 536 HIDAPI_DriverPS4_Rumble, 537 HIDAPI_DriverPS4_Update, 538 HIDAPI_DriverPS4_Quit 539}; 540 541#endif /* SDL_JOYSTICK_HIDAPI_PS4 */ 542 543#endif /* SDL_JOYSTICK_HIDAPI */ 544 545/* vi: set ts=4 sw=4 expandtab: */ 546[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.