Atlas - SDL_hidapi_xbox360.c

Home / ext / SDL / src / joystick / hidapi Lines: 1 | Size: 13613 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_HIDAPI 24 25#include "../../SDL_hints_c.h" 26#include "../SDL_sysjoystick.h" 27#include "SDL_hidapijoystick_c.h" 28#include "SDL_hidapi_rumble.h" 29 30#ifdef SDL_JOYSTICK_HIDAPI_XBOX360 31 32// Define this if you want to log all packets from the controller 33// #define DEBUG_XBOX_PROTOCOL 34 35typedef struct 36{ 37 SDL_HIDAPI_Device *device; 38 SDL_Joystick *joystick; 39 int player_index; 40 bool player_lights; 41 Uint8 last_state[USB_PACKET_LENGTH]; 42} SDL_DriverXbox360_Context; 43 44static void HIDAPI_DriverXbox360_RegisterHints(SDL_HintCallback callback, void *userdata) 45{ 46 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX, callback, userdata); 47 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX_360, callback, userdata); 48} 49 50static void HIDAPI_DriverXbox360_UnregisterHints(SDL_HintCallback callback, void *userdata) 51{ 52 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX, callback, userdata); 53 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX_360, callback, userdata); 54} 55 56static bool HIDAPI_DriverXbox360_IsEnabled(void) 57{ 58 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_XBOX_360, 59 SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_XBOX, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT))); 60} 61 62static bool HIDAPI_DriverXbox360_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GamepadType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol) 63{ 64 const int XB360W_IFACE_PROTOCOL = 129; // Wireless 65 66 if (vendor_id == USB_VENDOR_ASTRO && product_id == USB_PRODUCT_ASTRO_C40_XBOX360) { 67 // This is the ASTRO C40 in Xbox 360 mode 68 return true; 69 } 70 if (vendor_id == USB_VENDOR_NVIDIA) { 71 // This is the NVIDIA Shield controller which doesn't talk Xbox controller protocol 72 return false; 73 } 74 if ((vendor_id == USB_VENDOR_MICROSOFT && (product_id == USB_PRODUCT_XBOX360_WIRELESS_RECEIVER_THIRDPARTY2 || product_id == USB_PRODUCT_XBOX360_WIRELESS_RECEIVER)) || 75 (type == SDL_GAMEPAD_TYPE_XBOX360 && interface_protocol == XB360W_IFACE_PROTOCOL)) { 76 // This is the wireless dongle, which talks a different protocol 77 return false; 78 } 79 if (interface_number > 0) { 80 // This is the chatpad or other input interface, not the Xbox 360 interface 81 return false; 82 } 83#if defined(SDL_PLATFORM_MACOS) && defined(SDL_JOYSTICK_MFI) 84 if (SDL_IsJoystickSteamVirtualGamepad(vendor_id, product_id, version)) { 85 // GCController support doesn't work with the Steam Virtual Gamepad 86 return true; 87 } else { 88 // On macOS you can't write output reports to wired XBox controllers, 89 // so we'll just use the GCController support instead. 90 return false; 91 } 92#else 93 return (type == SDL_GAMEPAD_TYPE_XBOX360); 94#endif 95} 96 97static bool SetSlotLED(SDL_hid_device *dev, Uint8 slot, bool on) 98{ 99 const bool blink = false; 100 Uint8 mode = on ? ((blink ? 0x02 : 0x06) + slot) : 0; 101 Uint8 led_packet[] = { 0x01, 0x03, 0x00 }; 102 103 led_packet[2] = mode; 104 if (SDL_hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) { 105 return false; 106 } 107 return true; 108} 109 110static void UpdateSlotLED(SDL_DriverXbox360_Context *ctx) 111{ 112 if (ctx->player_lights && ctx->player_index >= 0) { 113 SetSlotLED(ctx->device->dev, (ctx->player_index % 4), true); 114 } else { 115 SetSlotLED(ctx->device->dev, 0, false); 116 } 117} 118 119static void SDLCALL SDL_PlayerLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 120{ 121 SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)userdata; 122 bool player_lights = SDL_GetStringBoolean(hint, true); 123 124 if (player_lights != ctx->player_lights) { 125 ctx->player_lights = player_lights; 126 127 UpdateSlotLED(ctx); 128 HIDAPI_UpdateDeviceProperties(ctx->device); 129 } 130} 131 132static bool HIDAPI_DriverXbox360_InitDevice(SDL_HIDAPI_Device *device) 133{ 134 SDL_DriverXbox360_Context *ctx; 135 136 ctx = (SDL_DriverXbox360_Context *)SDL_calloc(1, sizeof(*ctx)); 137 if (!ctx) { 138 return false; 139 } 140 ctx->device = device; 141 142 device->context = ctx; 143 144 device->type = SDL_GAMEPAD_TYPE_XBOX360; 145 146 if (SDL_IsJoystickSteamVirtualGamepad(device->vendor_id, device->product_id, device->version) && 147 device->product_string && SDL_strncmp(device->product_string, "GamePad-", 8) == 0) { 148 int slot = 0; 149 SDL_sscanf(device->product_string, "GamePad-%d", &slot); 150 device->steam_virtual_gamepad_slot = (slot - 1); 151 } 152 153 return HIDAPI_JoystickConnected(device, NULL); 154} 155 156static int HIDAPI_DriverXbox360_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id) 157{ 158 return -1; 159} 160 161static void HIDAPI_DriverXbox360_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index) 162{ 163 SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context; 164 165 if (!ctx->joystick) { 166 return; 167 } 168 169 ctx->player_index = player_index; 170 171 UpdateSlotLED(ctx); 172} 173 174static bool HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 175{ 176 SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context; 177 178 SDL_AssertJoysticksLocked(); 179 180 ctx->joystick = joystick; 181 SDL_zeroa(ctx->last_state); 182 183 // Initialize player index (needed for setting LEDs) 184 ctx->player_index = SDL_GetJoystickPlayerIndex(joystick); 185 ctx->player_lights = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_XBOX_360_PLAYER_LED, true); 186 UpdateSlotLED(ctx); 187 188 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX_360_PLAYER_LED, 189 SDL_PlayerLEDHintChanged, ctx); 190 191 // Initialize the joystick capabilities 192 joystick->nbuttons = 11; 193 joystick->naxes = SDL_GAMEPAD_AXIS_COUNT; 194 joystick->nhats = 1; 195 196 return true; 197} 198 199static bool HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 200{ 201 Uint8 rumble_packet[] = { 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 202 203 rumble_packet[3] = (low_frequency_rumble >> 8); 204 rumble_packet[4] = (high_frequency_rumble >> 8); 205 206 if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { 207 return SDL_SetError("Couldn't send rumble packet"); 208 } 209 return true; 210} 211 212static bool HIDAPI_DriverXbox360_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) 213{ 214 return SDL_Unsupported(); 215} 216 217static Uint32 HIDAPI_DriverXbox360_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 218{ 219 SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context; 220 Uint32 result = SDL_JOYSTICK_CAP_RUMBLE; 221 222 if (ctx->player_lights) { 223 result |= SDL_JOYSTICK_CAP_PLAYER_LED; 224 } 225 return result; 226} 227 228static bool HIDAPI_DriverXbox360_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) 229{ 230 return SDL_Unsupported(); 231} 232 233static bool HIDAPI_DriverXbox360_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size) 234{ 235 return SDL_Unsupported(); 236} 237 238static bool HIDAPI_DriverXbox360_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled) 239{ 240 return SDL_Unsupported(); 241} 242 243static void HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size) 244{ 245 Sint16 axis; 246#ifdef SDL_PLATFORM_MACOS 247 const bool invert_y_axes = false; 248#else 249 const bool invert_y_axes = true; 250#endif 251 Uint64 timestamp = SDL_GetTicksNS(); 252 253 if (ctx->last_state[2] != data[2]) { 254 Uint8 hat = 0; 255 256 if (data[2] & 0x01) { 257 hat |= SDL_HAT_UP; 258 } 259 if (data[2] & 0x02) { 260 hat |= SDL_HAT_DOWN; 261 } 262 if (data[2] & 0x04) { 263 hat |= SDL_HAT_LEFT; 264 } 265 if (data[2] & 0x08) { 266 hat |= SDL_HAT_RIGHT; 267 } 268 SDL_SendJoystickHat(timestamp, joystick, 0, hat); 269 270 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[2] & 0x10) != 0)); 271 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[2] & 0x20) != 0)); 272 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[2] & 0x40) != 0)); 273 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[2] & 0x80) != 0)); 274 } 275 276 if (ctx->last_state[3] != data[3]) { 277 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[3] & 0x01) != 0)); 278 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[3] & 0x02) != 0)); 279 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[3] & 0x04) != 0)); 280 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[3] & 0x10) != 0)); 281 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[3] & 0x20) != 0)); 282 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[3] & 0x40) != 0)); 283 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[3] & 0x80) != 0)); 284 } 285 286 axis = ((int)data[4] * 257) - 32768; 287 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis); 288 axis = ((int)data[5] * 257) - 32768; 289 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis); 290 axis = SDL_Swap16LE(*(Sint16 *)(&data[6])); 291 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis); 292 axis = SDL_Swap16LE(*(Sint16 *)(&data[8])); 293 if (invert_y_axes) { 294 axis = ~axis; 295 } 296 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis); 297 axis = SDL_Swap16LE(*(Sint16 *)(&data[10])); 298 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis); 299 axis = SDL_Swap16LE(*(Sint16 *)(&data[12])); 300 if (invert_y_axes) { 301 axis = ~axis; 302 } 303 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis); 304 305 SDL_memcpy(ctx->last_state, data, SDL_min((size_t)size, sizeof(ctx->last_state))); 306} 307 308static bool HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device) 309{ 310 SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context; 311 SDL_Joystick *joystick = NULL; 312 Uint8 data[USB_PACKET_LENGTH]; 313 int size = 0; 314 315 if (device->num_joysticks > 0) { 316 joystick = SDL_GetJoystickFromID(device->joysticks[0]); 317 } else { 318 return false; 319 } 320 321 while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { 322#ifdef DEBUG_XBOX_PROTOCOL 323 HIDAPI_DumpPacket("Xbox 360 packet: size = %d", data, size); 324#endif 325 if (!joystick) { 326 continue; 327 } 328 329 if (data[0] == 0x00) { 330 HIDAPI_DriverXbox360_HandleStatePacket(joystick, ctx, data, size); 331 } 332 } 333 334 if (size < 0) { 335 // Read error, device is disconnected 336 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 337 } 338 return (size >= 0); 339} 340 341static void HIDAPI_DriverXbox360_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 342{ 343 SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context; 344 345 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX_360_PLAYER_LED, 346 SDL_PlayerLEDHintChanged, ctx); 347 348 ctx->joystick = NULL; 349} 350 351static void HIDAPI_DriverXbox360_FreeDevice(SDL_HIDAPI_Device *device) 352{ 353} 354 355SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 = { 356 SDL_HINT_JOYSTICK_HIDAPI_XBOX_360, 357 true, 358 HIDAPI_DriverXbox360_RegisterHints, 359 HIDAPI_DriverXbox360_UnregisterHints, 360 HIDAPI_DriverXbox360_IsEnabled, 361 HIDAPI_DriverXbox360_IsSupportedDevice, 362 HIDAPI_DriverXbox360_InitDevice, 363 HIDAPI_DriverXbox360_GetDevicePlayerIndex, 364 HIDAPI_DriverXbox360_SetDevicePlayerIndex, 365 HIDAPI_DriverXbox360_UpdateDevice, 366 HIDAPI_DriverXbox360_OpenJoystick, 367 HIDAPI_DriverXbox360_RumbleJoystick, 368 HIDAPI_DriverXbox360_RumbleJoystickTriggers, 369 HIDAPI_DriverXbox360_GetJoystickCapabilities, 370 HIDAPI_DriverXbox360_SetJoystickLED, 371 HIDAPI_DriverXbox360_SendJoystickEffect, 372 HIDAPI_DriverXbox360_SetJoystickSensorsEnabled, 373 HIDAPI_DriverXbox360_CloseJoystick, 374 HIDAPI_DriverXbox360_FreeDevice, 375}; 376 377#endif // SDL_JOYSTICK_HIDAPI_XBOX360 378 379#endif // SDL_JOYSTICK_HIDAPI 380
[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.