SDL_hidapi_xbox360.c (12955B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> 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.h" 26 #include "SDL_events.h" 27 #include "SDL_timer.h" 28 #include "SDL_joystick.h" 29 #include "SDL_gamecontroller.h" 30 #include "../SDL_sysjoystick.h" 31 #include "SDL_hidapijoystick_c.h" 32 #include "SDL_hidapi_rumble.h" 33 34 35 #ifdef SDL_JOYSTICK_HIDAPI_XBOX360 36 37 /* Define this if you want to log all packets from the controller */ 38 /*#define DEBUG_XBOX_PROTOCOL*/ 39 40 41 typedef struct { 42 Uint8 last_state[USB_PACKET_LENGTH]; 43 } SDL_DriverXbox360_Context; 44 45 #if defined(__MACOSX__) 46 static SDL_bool 47 IsBluetoothXboxOneController(Uint16 vendor_id, Uint16 product_id) 48 { 49 /* Check to see if it's the Xbox One S or Xbox One Elite Series 2 in Bluetooth mode */ 50 if (vendor_id == USB_VENDOR_MICROSOFT) { 51 if (product_id == USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH || 52 product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLUETOOTH || 53 product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH) { 54 return SDL_TRUE; 55 } 56 } 57 return SDL_FALSE; 58 } 59 #endif 60 61 static SDL_bool 62 HIDAPI_DriverXbox360_IsSupportedDevice(const char *name, SDL_GameControllerType 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_NVIDIA) { 67 /* This is the NVIDIA Shield controller which doesn't talk Xbox controller protocol */ 68 return SDL_FALSE; 69 } 70 if ((vendor_id == USB_VENDOR_MICROSOFT && (product_id == 0x0291 || product_id == 0x0719)) || 71 (type == SDL_CONTROLLER_TYPE_XBOX360 && interface_protocol == XB360W_IFACE_PROTOCOL)) { 72 /* This is the wireless dongle, which talks a different protocol */ 73 return SDL_FALSE; 74 } 75 if (interface_number > 0) { 76 /* This is the chatpad or other input interface, not the Xbox 360 interface */ 77 return SDL_FALSE; 78 } 79 #if defined(__MACOSX__) || defined(__WIN32__) 80 if (vendor_id == USB_VENDOR_MICROSOFT && product_id == 0x028e && version == 1) { 81 /* This is the Steam Virtual Gamepad, which isn't supported by this driver */ 82 return SDL_FALSE; 83 } 84 #endif 85 #if defined(__MACOSX__) 86 /* Wired Xbox One controllers are handled by this driver, interfacing with 87 the 360Controller driver available from: 88 https://github.com/360Controller/360Controller/releases 89 90 Bluetooth Xbox One controllers are handled by the SDL Xbox One driver 91 */ 92 if (IsBluetoothXboxOneController(vendor_id, product_id)) { 93 return SDL_FALSE; 94 } 95 return (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE) ? SDL_TRUE : SDL_FALSE; 96 #else 97 return (type == SDL_CONTROLLER_TYPE_XBOX360) ? SDL_TRUE : SDL_FALSE; 98 #endif 99 } 100 101 static const char * 102 HIDAPI_DriverXbox360_GetDeviceName(Uint16 vendor_id, Uint16 product_id) 103 { 104 return NULL; 105 } 106 107 static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot) 108 { 109 Uint8 mode = 0x02 + slot; 110 const Uint8 led_packet[] = { 0x01, 0x03, mode }; 111 112 if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) { 113 return SDL_FALSE; 114 } 115 return SDL_TRUE; 116 } 117 118 static SDL_bool 119 HIDAPI_DriverXbox360_InitDevice(SDL_HIDAPI_Device *device) 120 { 121 return HIDAPI_JoystickConnected(device, NULL); 122 } 123 124 static int 125 HIDAPI_DriverXbox360_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id) 126 { 127 return -1; 128 } 129 130 static void 131 HIDAPI_DriverXbox360_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index) 132 { 133 if (!device->dev) { 134 return; 135 } 136 SetSlotLED(device->dev, (player_index % 4)); 137 } 138 139 static SDL_bool 140 HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 141 { 142 SDL_DriverXbox360_Context *ctx; 143 int player_index; 144 145 ctx = (SDL_DriverXbox360_Context *)SDL_calloc(1, sizeof(*ctx)); 146 if (!ctx) { 147 SDL_OutOfMemory(); 148 return SDL_FALSE; 149 } 150 151 device->dev = hid_open_path(device->path, 0); 152 if (!device->dev) { 153 SDL_SetError("Couldn't open %s", device->path); 154 SDL_free(ctx); 155 return SDL_FALSE; 156 } 157 device->context = ctx; 158 159 /* Set the controller LED */ 160 player_index = SDL_JoystickGetPlayerIndex(joystick); 161 if (player_index >= 0) { 162 SetSlotLED(device->dev, (player_index % 4)); 163 } 164 165 /* Initialize the joystick capabilities */ 166 joystick->nbuttons = 15; 167 joystick->naxes = SDL_CONTROLLER_AXIS_MAX; 168 joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; 169 170 return SDL_TRUE; 171 } 172 173 static int 174 HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 175 { 176 #ifdef __MACOSX__ 177 if (IsBluetoothXboxOneController(device->vendor_id, device->product_id)) { 178 Uint8 rumble_packet[] = { 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00 }; 179 180 rumble_packet[4] = (low_frequency_rumble >> 8); 181 rumble_packet[5] = (high_frequency_rumble >> 8); 182 183 if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { 184 return SDL_SetError("Couldn't send rumble packet"); 185 } 186 } else { 187 /* On Mac OS X the 360Controller driver uses this short report, 188 and we need to prefix it with a magic token so hidapi passes it through untouched 189 */ 190 Uint8 rumble_packet[] = { 'M', 'A', 'G', 'I', 'C', '0', 0x00, 0x04, 0x00, 0x00 }; 191 192 rumble_packet[6+2] = (low_frequency_rumble >> 8); 193 rumble_packet[6+3] = (high_frequency_rumble >> 8); 194 195 if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { 196 return SDL_SetError("Couldn't send rumble packet"); 197 } 198 } 199 #else 200 Uint8 rumble_packet[] = { 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 201 202 rumble_packet[3] = (low_frequency_rumble >> 8); 203 rumble_packet[4] = (high_frequency_rumble >> 8); 204 205 if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { 206 return SDL_SetError("Couldn't send rumble packet"); 207 } 208 #endif 209 return 0; 210 } 211 212 static int 213 HIDAPI_DriverXbox360_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) 214 { 215 return SDL_Unsupported(); 216 } 217 218 static SDL_bool 219 HIDAPI_DriverXbox360_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 220 { 221 /* Doesn't have an RGB LED, so don't return true here */ 222 return SDL_FALSE; 223 } 224 225 static int 226 HIDAPI_DriverXbox360_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) 227 { 228 return SDL_Unsupported(); 229 } 230 231 static int 232 HIDAPI_DriverXbox360_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled) 233 { 234 return SDL_Unsupported(); 235 } 236 237 static void 238 HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size) 239 { 240 Sint16 axis; 241 #ifdef __MACOSX__ 242 const SDL_bool invert_y_axes = SDL_FALSE; 243 #else 244 const SDL_bool invert_y_axes = SDL_TRUE; 245 #endif 246 247 if (ctx->last_state[2] != data[2]) { 248 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED); 249 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED); 250 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[2] & 0x04) ? SDL_PRESSED : SDL_RELEASED); 251 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[2] & 0x08) ? SDL_PRESSED : SDL_RELEASED); 252 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[2] & 0x10) ? SDL_PRESSED : SDL_RELEASED); 253 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[2] & 0x20) ? SDL_PRESSED : SDL_RELEASED); 254 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[2] & 0x40) ? SDL_PRESSED : SDL_RELEASED); 255 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x80) ? SDL_PRESSED : SDL_RELEASED); 256 } 257 258 if (ctx->last_state[3] != data[3]) { 259 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x01) ? SDL_PRESSED : SDL_RELEASED); 260 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x02) ? SDL_PRESSED : SDL_RELEASED); 261 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED); 262 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[3] & 0x10) ? SDL_PRESSED : SDL_RELEASED); 263 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[3] & 0x20) ? SDL_PRESSED : SDL_RELEASED); 264 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[3] & 0x40) ? SDL_PRESSED : SDL_RELEASED); 265 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[3] & 0x80) ? SDL_PRESSED : SDL_RELEASED); 266 } 267 268 axis = ((int)data[4] * 257) - 32768; 269 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis); 270 axis = ((int)data[5] * 257) - 32768; 271 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis); 272 axis = *(Sint16*)(&data[6]); 273 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis); 274 axis = *(Sint16*)(&data[8]); 275 if (invert_y_axes) { 276 axis = ~axis; 277 } 278 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis); 279 axis = *(Sint16*)(&data[10]); 280 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis); 281 axis = *(Sint16*)(&data[12]); 282 if (invert_y_axes) { 283 axis = ~axis; 284 } 285 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis); 286 287 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state))); 288 } 289 290 static SDL_bool 291 HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device) 292 { 293 SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context; 294 SDL_Joystick *joystick = NULL; 295 Uint8 data[USB_PACKET_LENGTH]; 296 int size = 0; 297 298 if (device->num_joysticks > 0) { 299 joystick = SDL_JoystickFromInstanceID(device->joysticks[0]); 300 } 301 if (!joystick) { 302 return SDL_FALSE; 303 } 304 305 while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { 306 #ifdef DEBUG_XBOX_PROTOCOL 307 HIDAPI_DumpPacket("Xbox 360 packet: size = %d", data, size); 308 #endif 309 if (data[0] == 0x00) { 310 HIDAPI_DriverXbox360_HandleStatePacket(joystick, ctx, data, size); 311 } 312 } 313 314 if (size < 0) { 315 /* Read error, device is disconnected */ 316 HIDAPI_JoystickDisconnected(device, joystick->instance_id); 317 } 318 return (size >= 0); 319 } 320 321 static void 322 HIDAPI_DriverXbox360_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 323 { 324 if (device->dev) { 325 hid_close(device->dev); 326 device->dev = NULL; 327 } 328 329 SDL_free(device->context); 330 device->context = NULL; 331 } 332 333 static void 334 HIDAPI_DriverXbox360_FreeDevice(SDL_HIDAPI_Device *device) 335 { 336 } 337 338 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 = 339 { 340 SDL_HINT_JOYSTICK_HIDAPI_XBOX, 341 SDL_TRUE, 342 HIDAPI_DriverXbox360_IsSupportedDevice, 343 HIDAPI_DriverXbox360_GetDeviceName, 344 HIDAPI_DriverXbox360_InitDevice, 345 HIDAPI_DriverXbox360_GetDevicePlayerIndex, 346 HIDAPI_DriverXbox360_SetDevicePlayerIndex, 347 HIDAPI_DriverXbox360_UpdateDevice, 348 HIDAPI_DriverXbox360_OpenJoystick, 349 HIDAPI_DriverXbox360_RumbleJoystick, 350 HIDAPI_DriverXbox360_RumbleJoystickTriggers, 351 HIDAPI_DriverXbox360_HasJoystickLED, 352 HIDAPI_DriverXbox360_SetJoystickLED, 353 HIDAPI_DriverXbox360_SetJoystickSensorsEnabled, 354 HIDAPI_DriverXbox360_CloseJoystick, 355 HIDAPI_DriverXbox360_FreeDevice, 356 }; 357 358 #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */ 359 360 #endif /* SDL_JOYSTICK_HIDAPI */ 361 362 /* vi: set ts=4 sw=4 expandtab: */