2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "../../SDL_internal.h"
23 #ifdef SDL_JOYSTICK_HIDAPI
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"
35 #ifdef SDL_JOYSTICK_HIDAPI_XBOX360
37 /* Define this if you want to log all packets from the controller */
38 /*#define DEBUG_XBOX_PROTOCOL*/
42 Uint8 last_state[USB_PACKET_LENGTH];
43 } SDL_DriverXbox360_Context;
45 #if defined(__MACOSX__)
47 IsBluetoothXboxOneController(Uint16 vendor_id, Uint16 product_id)
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) {
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)
64 const int XB360W_IFACE_PROTOCOL = 129; /* Wireless */
66 if (vendor_id == USB_VENDOR_NVIDIA) {
67 /* This is the NVIDIA Shield controller which doesn't talk Xbox controller protocol */
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 */
75 if (interface_number > 0) {
76 /* This is the chatpad or other input interface, not the Xbox 360 interface */
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 */
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
90 Bluetooth Xbox One controllers are handled by the SDL Xbox One driver
92 if (IsBluetoothXboxOneController(vendor_id, product_id)) {
95 return (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE) ? SDL_TRUE : SDL_FALSE;
97 return (type == SDL_CONTROLLER_TYPE_XBOX360) ? SDL_TRUE : SDL_FALSE;
102 HIDAPI_DriverXbox360_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
107 static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot)
109 Uint8 mode = 0x02 + slot;
110 const Uint8 led_packet[] = { 0x01, 0x03, mode };
112 if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
119 HIDAPI_DriverXbox360_InitDevice(SDL_HIDAPI_Device *device)
121 return HIDAPI_JoystickConnected(device, NULL);
125 HIDAPI_DriverXbox360_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
131 HIDAPI_DriverXbox360_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
136 SetSlotLED(device->dev, (player_index % 4));
140 HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
142 SDL_DriverXbox360_Context *ctx;
145 ctx = (SDL_DriverXbox360_Context *)SDL_calloc(1, sizeof(*ctx));
151 device->dev = hid_open_path(device->path, 0);
153 SDL_SetError("Couldn't open %s", device->path);
157 device->context = ctx;
159 /* Set the controller LED */
160 player_index = SDL_JoystickGetPlayerIndex(joystick);
161 if (player_index >= 0) {
162 SetSlotLED(device->dev, (player_index % 4));
165 /* Initialize the joystick capabilities */
166 joystick->nbuttons = 15;
167 joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
168 joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
174 HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
177 if (IsBluetoothXboxOneController(device->vendor_id, device->product_id)) {
178 Uint8 rumble_packet[] = { 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00 };
180 rumble_packet[4] = (low_frequency_rumble >> 8);
181 rumble_packet[5] = (high_frequency_rumble >> 8);
183 if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
184 return SDL_SetError("Couldn't send rumble packet");
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
190 Uint8 rumble_packet[] = { 'M', 'A', 'G', 'I', 'C', '0', 0x00, 0x04, 0x00, 0x00 };
192 rumble_packet[6+2] = (low_frequency_rumble >> 8);
193 rumble_packet[6+3] = (high_frequency_rumble >> 8);
195 if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
196 return SDL_SetError("Couldn't send rumble packet");
200 Uint8 rumble_packet[] = { 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
202 rumble_packet[3] = (low_frequency_rumble >> 8);
203 rumble_packet[4] = (high_frequency_rumble >> 8);
205 if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
206 return SDL_SetError("Couldn't send rumble packet");
213 HIDAPI_DriverXbox360_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
215 return SDL_Unsupported();
219 HIDAPI_DriverXbox360_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
221 /* Doesn't have an RGB LED, so don't return true here */
226 HIDAPI_DriverXbox360_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
228 return SDL_Unsupported();
232 HIDAPI_DriverXbox360_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
234 return SDL_Unsupported();
238 HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
242 const SDL_bool invert_y_axes = SDL_FALSE;
244 const SDL_bool invert_y_axes = SDL_TRUE;
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);
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);
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]);
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]);
285 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
287 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
291 HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device)
293 SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
294 SDL_Joystick *joystick = NULL;
295 Uint8 data[USB_PACKET_LENGTH];
298 if (device->num_joysticks > 0) {
299 joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
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);
309 if (data[0] == 0x00) {
310 HIDAPI_DriverXbox360_HandleStatePacket(joystick, ctx, data, size);
315 /* Read error, device is disconnected */
316 HIDAPI_JoystickDisconnected(device, joystick->instance_id);
322 HIDAPI_DriverXbox360_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
325 hid_close(device->dev);
329 SDL_free(device->context);
330 device->context = NULL;
334 HIDAPI_DriverXbox360_FreeDevice(SDL_HIDAPI_Device *device)
338 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 =
340 SDL_HINT_JOYSTICK_HIDAPI_XBOX,
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,
358 #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
360 #endif /* SDL_JOYSTICK_HIDAPI */
362 /* vi: set ts=4 sw=4 expandtab: */