SDL  2.0
SDL_hidapi_xbox360.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2019 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_log.h"
27 #include "SDL_events.h"
28 #include "SDL_timer.h"
29 #include "SDL_joystick.h"
30 #include "SDL_gamecontroller.h"
31 #include "../SDL_sysjoystick.h"
32 #include "SDL_hidapijoystick_c.h"
33 
34 
35 #ifdef SDL_JOYSTICK_HIDAPI_XBOX360
36 
37 #ifdef __WIN32__
38 #define SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
39 /* This requires the Windows 10 SDK to build */
40 /*#define SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT*/
41 #endif
42 
43 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
44 #include "../../core/windows/SDL_xinput.h"
45 #endif
46 
47 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
48 #include "../../core/windows/SDL_windows.h"
49 #define COBJMACROS
50 #include "windows.gaming.input.h"
51 #endif
52 
53 #define USB_PACKET_LENGTH 64
54 
55 
56 typedef struct {
57  Uint8 last_state[USB_PACKET_LENGTH];
58  Uint32 rumble_expiration;
59 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
60  SDL_bool xinput_enabled;
61  Uint8 xinput_slot;
62 #endif
63 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
64  SDL_bool coinitialized;
65  __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics;
66  __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
67  struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration;
68 #endif
69 } SDL_DriverXbox360_Context;
70 
71 
72 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
73 static Uint8 xinput_slots;
74 
75 static void
76 HIDAPI_DriverXbox360_MarkXInputSlotUsed(Uint8 xinput_slot)
77 {
78  if (xinput_slot != XUSER_INDEX_ANY) {
79  xinput_slots |= (0x01 << xinput_slot);
80  }
81 }
82 
83 static void
84 HIDAPI_DriverXbox360_MarkXInputSlotFree(Uint8 xinput_slot)
85 {
86  if (xinput_slot != XUSER_INDEX_ANY) {
87  xinput_slots &= ~(0x01 << xinput_slot);
88  }
89 }
90 
91 static SDL_bool
92 HIDAPI_DriverXbox360_MissingXInputSlot()
93 {
94  return xinput_slots != 0x0F;
95 }
96 
97 static Uint8
98 HIDAPI_DriverXbox360_GuessXInputSlot(WORD wButtons)
99 {
100  DWORD user_index;
101  int match_count;
102  Uint8 match_slot;
103 
104  if (!XINPUTGETSTATE) {
105  return XUSER_INDEX_ANY;
106  }
107 
108  match_count = 0;
109  for (user_index = 0; user_index < XUSER_MAX_COUNT; ++user_index) {
110  XINPUT_STATE_EX xinput_state;
111 
112  if (XINPUTGETSTATE(user_index, &xinput_state) == ERROR_SUCCESS) {
113  if (xinput_state.Gamepad.wButtons == wButtons) {
114  ++match_count;
115  match_slot = (Uint8)user_index;
116  }
117  }
118  }
119  if (match_count == 1) {
120  return match_slot;
121  }
122  return XUSER_INDEX_ANY;
123 }
124 
125 #endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
126 
127 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
128 
129 static void
130 HIDAPI_DriverXbox360_InitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
131 {
132  /* I think this takes care of RoInitialize() in a way that is compatible with the rest of SDL */
133  if (FAILED(WIN_CoInitialize())) {
134  return;
135  }
136  ctx->coinitialized = SDL_TRUE;
137 
138  {
139  static const IID SDL_IID_IGamepadStatics = { 0x8BBCE529, 0xD49C, 0x39E9, { 0x95, 0x60, 0xE4, 0x7D, 0xDE, 0x96, 0xB7, 0xC8 } };
140  HRESULT hr;
141  HMODULE hModule = LoadLibraryA("combase.dll");
142  if (hModule != NULL) {
143  typedef HRESULT (WINAPI *WindowsCreateString_t)(PCNZWCH sourceString, UINT32 length, HSTRING* string);
144  typedef HRESULT (WINAPI *WindowsDeleteString_t)(HSTRING string);
145  typedef HRESULT (WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void** factory);
146 
147  WindowsCreateString_t WindowsCreateStringFunc = (WindowsCreateString_t)GetProcAddress(hModule, "WindowsCreateString");
148  WindowsDeleteString_t WindowsDeleteStringFunc = (WindowsDeleteString_t)GetProcAddress(hModule, "WindowsDeleteString");
149  RoGetActivationFactory_t RoGetActivationFactoryFunc = (RoGetActivationFactory_t)GetProcAddress(hModule, "RoGetActivationFactory");
150  if (WindowsCreateStringFunc && WindowsDeleteStringFunc && RoGetActivationFactoryFunc) {
151  LPTSTR pNamespace = L"Windows.Gaming.Input.Gamepad";
152  HSTRING hNamespaceString;
153 
154  hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString);
155  if (SUCCEEDED(hr)) {
156  RoGetActivationFactoryFunc(hNamespaceString, &SDL_IID_IGamepadStatics, &ctx->gamepad_statics);
157  WindowsDeleteStringFunc(hNamespaceString);
158  }
159  }
160  FreeLibrary(hModule);
161  }
162  }
163 }
164 
165 static Uint8
166 HIDAPI_DriverXbox360_GetGamepadButtonsForMatch(__x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad)
167 {
168  HRESULT hr;
169  struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state;
170  Uint8 buttons = 0;
171 
172  hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(gamepad, &state);
173  if (SUCCEEDED(hr)) {
174  if (state.Buttons & GamepadButtons_A) {
175  buttons |= (1 << SDL_CONTROLLER_BUTTON_A);
176  }
177  if (state.Buttons & GamepadButtons_B) {
178  buttons |= (1 << SDL_CONTROLLER_BUTTON_B);
179  }
180  if (state.Buttons & GamepadButtons_X) {
181  buttons |= (1 << SDL_CONTROLLER_BUTTON_X);
182  }
183  if (state.Buttons & GamepadButtons_Y) {
184  buttons |= (1 << SDL_CONTROLLER_BUTTON_Y);
185  }
186  }
187  return buttons;
188 }
189 
190 static void
191 HIDAPI_DriverXbox360_GuessGamepad(SDL_DriverXbox360_Context *ctx, Uint8 buttons)
192 {
193  HRESULT hr;
194  __FIVectorView_1_Windows__CGaming__CInput__CGamepad *gamepads;
195 
196  hr = __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_get_Gamepads(ctx->gamepad_statics, &gamepads);
197  if (SUCCEEDED(hr)) {
198  unsigned int i, num_gamepads;
199 
200  hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_get_Size(gamepads, &num_gamepads);
201  if (SUCCEEDED(hr)) {
202  int match_count;
203  unsigned int match_slot;
204 
205  match_count = 0;
206  for (i = 0; i < num_gamepads; ++i) {
207  __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
208 
209  hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, i, &gamepad);
210  if (SUCCEEDED(hr)) {
211  Uint8 gamepad_buttons = HIDAPI_DriverXbox360_GetGamepadButtonsForMatch(gamepad);
212  if (buttons == gamepad_buttons) {
213  ++match_count;
214  match_slot = i;
215  }
216  __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad);
217  }
218  }
219  if (match_count == 1) {
220  hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, match_slot, &ctx->gamepad);
221  if (SUCCEEDED(hr)) {
222  }
223  }
224  }
225  __FIVectorView_1_Windows__CGaming__CInput__CGamepad_Release(gamepads);
226  }
227 }
228 
229 static void
230 HIDAPI_DriverXbox360_QuitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
231 {
232  if (ctx->gamepad_statics) {
233  __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(ctx->gamepad_statics);
234  ctx->gamepad_statics = NULL;
235  }
236  if (ctx->gamepad) {
237  __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(ctx->gamepad);
238  ctx->gamepad = NULL;
239  }
240 
241  if (ctx->coinitialized) {
243  ctx->coinitialized = SDL_FALSE;
244  }
245 }
246 
247 #endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
248 
249 static SDL_bool
250 HIDAPI_DriverXbox360_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number)
251 {
252 #if defined(__MACOSX__) || defined(__WIN32__)
253  if (vendor_id == 0x045e && product_id == 0x028e && version == 1) {
254  /* This is the Steam Virtual Gamepad, which isn't supported by this driver */
255  return SDL_FALSE;
256  }
257  if (vendor_id == 0x045e && product_id == 0x02e0) {
258  /* This is the old Bluetooth Xbox One S firmware, which isn't supported by this driver */
259  return SDL_FALSE;
260  }
261  return SDL_IsJoystickXbox360(vendor_id, product_id) || SDL_IsJoystickXboxOne(vendor_id, product_id);
262 #else
263  return SDL_IsJoystickXbox360(vendor_id, product_id);
264 #endif
265 }
266 
267 static const char *
268 HIDAPI_DriverXbox360_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
269 {
270  return HIDAPI_XboxControllerName(vendor_id, product_id);
271 }
272 
273 static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot)
274 {
275  const Uint8 led_packet[] = { 0x01, 0x03, (2 + slot) };
276 
277  if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
278  return SDL_FALSE;
279  }
280  return SDL_TRUE;
281 }
282 
283 static SDL_bool
284 HIDAPI_DriverXbox360_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
285 {
286  SDL_DriverXbox360_Context *ctx;
287 
288  ctx = (SDL_DriverXbox360_Context *)SDL_calloc(1, sizeof(*ctx));
289  if (!ctx) {
290  SDL_OutOfMemory();
291  return SDL_FALSE;
292  }
293 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
295  if (ctx->xinput_enabled && WIN_LoadXInputDLL() < 0) {
296  ctx->xinput_enabled = SDL_FALSE;
297  }
298  ctx->xinput_slot = XUSER_INDEX_ANY;
299 #endif
300 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
301  HIDAPI_DriverXbox360_InitWindowsGamingInput(ctx);
302 #endif
303  *context = ctx;
304 
305  /* Set the controller LED */
306  SetSlotLED(dev, (joystick->instance_id % 4));
307 
308  /* Initialize the joystick capabilities */
309  joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
310  joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
311  joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
312 
313  return SDL_TRUE;
314 }
315 
316 static int
317 HIDAPI_DriverXbox360_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
318 {
319  SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context;
320 
321 #ifdef __WIN32__
322  SDL_bool rumbled = SDL_FALSE;
323 
324 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
325  if (!rumbled && ctx->gamepad) {
326  HRESULT hr;
327 
328  ctx->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;
329  ctx->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;
330  hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(ctx->gamepad, ctx->vibration);
331  if (SUCCEEDED(hr)) {
332  rumbled = SDL_TRUE;
333  }
334  }
335 #endif
336 
337 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
338  if (!rumbled && ctx->xinput_slot != XUSER_INDEX_ANY) {
339  XINPUT_VIBRATION XVibration;
340 
341  if (!XINPUTSETSTATE) {
342  return SDL_Unsupported();
343  }
344 
345  XVibration.wLeftMotorSpeed = low_frequency_rumble;
346  XVibration.wRightMotorSpeed = high_frequency_rumble;
347  if (XINPUTSETSTATE(ctx->xinput_slot, &XVibration) == ERROR_SUCCESS) {
348  rumbled = SDL_TRUE;
349  } else {
350  return SDL_SetError("XInputSetState() failed");
351  }
352  }
353 #endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
354 
355 #else /* !__WIN32__ */
356 
357 #ifdef __MACOSX__
358  /* On Mac OS X the 360Controller driver uses this short report,
359  and we need to prefix it with a magic token so hidapi passes it through untouched
360  */
361  Uint8 rumble_packet[] = { 'M', 'A', 'G', 'I', 'C', '0', 0x00, 0x04, 0x00, 0x00 };
362 
363  rumble_packet[6+2] = (low_frequency_rumble >> 8);
364  rumble_packet[6+3] = (high_frequency_rumble >> 8);
365 #else
366  Uint8 rumble_packet[] = { 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
367 
368  rumble_packet[3] = (low_frequency_rumble >> 8);
369  rumble_packet[4] = (high_frequency_rumble >> 8);
370 #endif
371 
372  if (hid_write(dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
373  return SDL_SetError("Couldn't send rumble packet");
374  }
375 #endif /* __WIN32__ */
376 
377  if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
378  ctx->rumble_expiration = SDL_GetTicks() + duration_ms;
379  } else {
380  ctx->rumble_expiration = 0;
381  }
382  return 0;
383 }
384 
385 #ifdef __WIN32__
386  /* This is the packet format for Xbox 360 and Xbox One controllers on Windows,
387  however with this interface there is no rumble support, no guide button,
388  and the left and right triggers are tied together as a single axis.
389 
390  We use XInput and Windows.Gaming.Input to make up for these shortcomings.
391  */
392 static void
393 HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
394 {
395  Sint16 axis;
396  SDL_bool has_trigger_data = SDL_FALSE;
397 
398  if (ctx->last_state[10] != data[10]) {
407  }
408 
409  if (ctx->last_state[11] != data[11]) {
410  SDL_bool dpad_up = SDL_FALSE;
411  SDL_bool dpad_down = SDL_FALSE;
412  SDL_bool dpad_left = SDL_FALSE;
413  SDL_bool dpad_right = SDL_FALSE;
414 
417 
418  switch (data[11] & 0x3C) {
419  case 4:
420  dpad_up = SDL_TRUE;
421  break;
422  case 8:
423  dpad_up = SDL_TRUE;
424  dpad_right = SDL_TRUE;
425  break;
426  case 12:
427  dpad_right = SDL_TRUE;
428  break;
429  case 16:
430  dpad_right = SDL_TRUE;
431  dpad_down = SDL_TRUE;
432  break;
433  case 20:
434  dpad_down = SDL_TRUE;
435  break;
436  case 24:
437  dpad_left = SDL_TRUE;
438  dpad_down = SDL_TRUE;
439  break;
440  case 28:
441  dpad_left = SDL_TRUE;
442  break;
443  case 32:
444  dpad_up = SDL_TRUE;
445  dpad_left = SDL_TRUE;
446  break;
447  default:
448  break;
449  }
454  }
455 
456  axis = (int)*(Uint16*)(&data[0]) - 0x8000;
458  axis = (int)*(Uint16*)(&data[2]) - 0x8000;
460  axis = (int)*(Uint16*)(&data[4]) - 0x8000;
462  axis = (int)*(Uint16*)(&data[6]) - 0x8000;
464 
465 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
466  if (ctx->gamepad_statics && !ctx->gamepad) {
467  Uint8 buttons = 0;
468 
469  if (data[10] & 0x01) {
470  buttons |= (1 << SDL_CONTROLLER_BUTTON_A);
471  }
472  if (data[10] & 0x02) {
473  buttons |= (1 << SDL_CONTROLLER_BUTTON_B);
474  }
475  if (data[10] & 0x04) {
476  buttons |= (1 << SDL_CONTROLLER_BUTTON_X);
477  }
478  if (data[10] & 0x08) {
479  buttons |= (1 << SDL_CONTROLLER_BUTTON_Y);
480  }
481  if (buttons != 0) {
482  HIDAPI_DriverXbox360_GuessGamepad(ctx, buttons);
483  }
484  }
485 
486  if (ctx->gamepad) {
487  HRESULT hr;
488  struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state;
489 
490  hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(ctx->gamepad, &state);
491  if (SUCCEEDED(hr)) {
493  SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)(state.LeftTrigger * SDL_MAX_UINT16)) - 32768);
494  SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)(state.RightTrigger * SDL_MAX_UINT16)) - 32768);
495  has_trigger_data = SDL_TRUE;
496  }
497  }
498 #endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
499 
500 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
501  if (ctx->xinput_enabled) {
502  if (ctx->xinput_slot == XUSER_INDEX_ANY && HIDAPI_DriverXbox360_MissingXInputSlot()) {
503  WORD wButtons = 0;
504 
505  if (data[10] & 0x01) {
506  wButtons |= XINPUT_GAMEPAD_A;
507  }
508  if (data[10] & 0x02) {
509  wButtons |= XINPUT_GAMEPAD_B;
510  }
511  if (data[10] & 0x04) {
512  wButtons |= XINPUT_GAMEPAD_X;
513  }
514  if (data[10] & 0x08) {
515  wButtons |= XINPUT_GAMEPAD_Y;
516  }
517  if (wButtons != 0) {
518  Uint8 xinput_slot = HIDAPI_DriverXbox360_GuessXInputSlot(wButtons);
519  if (xinput_slot != XUSER_INDEX_ANY) {
520  HIDAPI_DriverXbox360_MarkXInputSlotUsed(xinput_slot);
521  ctx->xinput_slot = xinput_slot;
522  }
523  }
524  }
525 
526  if (!has_trigger_data && ctx->xinput_slot != XUSER_INDEX_ANY) {
527  XINPUT_STATE_EX xinput_state;
528 
529  if (XINPUTGETSTATE(ctx->xinput_slot, &xinput_state) == ERROR_SUCCESS) {
530  SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE) ? SDL_PRESSED : SDL_RELEASED);
531  SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)xinput_state.Gamepad.bLeftTrigger * 257) - 32768);
532  SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)xinput_state.Gamepad.bRightTrigger * 257) - 32768);
533  has_trigger_data = SDL_TRUE;
534  }
535  }
536  }
537 #endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
538 
539  if (!has_trigger_data) {
540  axis = (data[9] * 257) - 32768;
541  if (data[9] < 0x80) {
542  axis = -axis * 2 - 32769;
544  } else if (data[9] > 0x80) {
545  axis = axis * 2 - 32767;
547  } else {
550  }
551  }
552 
553  SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
554 }
555 #else
556 
557 static void
558 HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
559 {
560  Sint16 axis;
561 #ifdef __MACOSX__
562  const SDL_bool invert_y_axes = SDL_FALSE;
563 #else
564  const SDL_bool invert_y_axes = SDL_TRUE;
565 #endif
566 
567  if (ctx->last_state[2] != data[2]) {
576  }
577 
578  if (ctx->last_state[3] != data[3]) {
586  }
587 
588  axis = ((int)data[4] * 257) - 32768;
590  axis = ((int)data[5] * 257) - 32768;
592  axis = *(Sint16*)(&data[6]);
594  axis = *(Sint16*)(&data[8]);
595  if (invert_y_axes) {
596  axis = ~axis;
597  }
599  axis = *(Sint16*)(&data[10]);
601  axis = *(Sint16*)(&data[12]);
602  if (invert_y_axes) {
603  axis = ~axis;
604  }
606 
607  SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
608 }
609 #endif /* __WIN32__ */
610 
611 #ifdef __MACOSX__
612 static void
613 HIDAPI_DriverXboxOneS_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
614 {
615  Sint16 axis;
616 
617  if (ctx->last_state[14] != data[14]) {
624  }
625 
626  if (ctx->last_state[15] != data[15]) {
630  }
631 
632  if (ctx->last_state[16] != data[16]) {
634  }
635 
636  if (ctx->last_state[13] != data[13]) {
637  SDL_bool dpad_up = SDL_FALSE;
638  SDL_bool dpad_down = SDL_FALSE;
639  SDL_bool dpad_left = SDL_FALSE;
640  SDL_bool dpad_right = SDL_FALSE;
641 
642  switch (data[13]) {
643  case 1:
644  dpad_up = SDL_TRUE;
645  break;
646  case 2:
647  dpad_up = SDL_TRUE;
648  dpad_right = SDL_TRUE;
649  break;
650  case 3:
651  dpad_right = SDL_TRUE;
652  break;
653  case 4:
654  dpad_right = SDL_TRUE;
655  dpad_down = SDL_TRUE;
656  break;
657  case 5:
658  dpad_down = SDL_TRUE;
659  break;
660  case 6:
661  dpad_left = SDL_TRUE;
662  dpad_down = SDL_TRUE;
663  break;
664  case 7:
665  dpad_left = SDL_TRUE;
666  break;
667  case 8:
668  dpad_up = SDL_TRUE;
669  dpad_left = SDL_TRUE;
670  break;
671  default:
672  break;
673  }
678  }
679 
680  axis = (int)*(Uint16*)(&data[1]) - 0x8000;
682  axis = (int)*(Uint16*)(&data[3]) - 0x8000;
684  axis = (int)*(Uint16*)(&data[5]) - 0x8000;
686  axis = (int)*(Uint16*)(&data[7]) - 0x8000;
688 
689  axis = ((int)*(Sint16*)(&data[9]) * 64) - 32768;
690  if (axis == 32704) {
691  axis = 32767;
692  }
694 
695  axis = ((int)*(Sint16*)(&data[11]) * 64) - 32768;
696  if (axis == 32704) {
697  axis = 32767;
698  }
700 
701  SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
702 }
703 
704 static void
705 HIDAPI_DriverXboxOneS_HandleGuidePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
706 {
708 }
709 #endif /* __MACOSX__ */
710 
711 static SDL_bool
712 HIDAPI_DriverXbox360_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
713 {
714  SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context;
715  Uint8 data[USB_PACKET_LENGTH];
716  int size;
717 
718  while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) {
719 #ifdef __WIN32__
720  HIDAPI_DriverXbox360_HandleStatePacket(joystick, dev, ctx, data, size);
721 #else
722  switch (data[0]) {
723  case 0x00:
724  HIDAPI_DriverXbox360_HandleStatePacket(joystick, dev, ctx, data, size);
725  break;
726 #ifdef __MACOSX__
727  case 0x01:
728  HIDAPI_DriverXboxOneS_HandleStatePacket(joystick, dev, ctx, data, size);
729  break;
730  case 0x02:
731  HIDAPI_DriverXboxOneS_HandleGuidePacket(joystick, dev, ctx, data, size);
732  break;
733 #endif
734  default:
735 #ifdef DEBUG_JOYSTICK
736  SDL_Log("Unknown Xbox 360 packet, size = %d\n", size);
737  SDL_Log("%.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n",
738  data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
739  data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15], data[16]);
740 #endif
741  break;
742  }
743 #endif /* __WIN32__ */
744  }
745 
746  if (ctx->rumble_expiration) {
747  Uint32 now = SDL_GetTicks();
748  if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
749  HIDAPI_DriverXbox360_Rumble(joystick, dev, context, 0, 0, 0);
750  }
751  }
752 
753  return (size >= 0);
754 }
755 
756 static void
757 HIDAPI_DriverXbox360_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
758 {
759 #if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT)
760  SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context;
761 #endif
762 
763 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
764  if (ctx->xinput_enabled) {
765  HIDAPI_DriverXbox360_MarkXInputSlotFree(ctx->xinput_slot);
766  WIN_UnloadXInputDLL();
767  }
768 #endif
769 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
770  HIDAPI_DriverXbox360_InitWindowsGamingInput(ctx);
771 #endif
772  SDL_free(context);
773 }
774 
776 {
778  SDL_TRUE,
779  HIDAPI_DriverXbox360_IsSupportedDevice,
780  HIDAPI_DriverXbox360_GetDeviceName,
781  HIDAPI_DriverXbox360_Init,
782  HIDAPI_DriverXbox360_Rumble,
783  HIDAPI_DriverXbox360_Update,
784  HIDAPI_DriverXbox360_Quit
785 };
786 
787 #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
788 
789 #endif /* SDL_JOYSTICK_HIDAPI */
790 
791 /* vi: set ts=4 sw=4 expandtab: */
SDL_CONTROLLER_BUTTON_DPAD_LEFT
Definition: SDL_gamecontroller.h:332
SDL_CONTROLLER_AXIS_RIGHTX
Definition: SDL_gamecontroller.h:277
SDL_events.h
hid_write
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length)
Write an Output report to a HID device.
NULL
#define NULL
Definition: begin_code.h:167
SDL_timer.h
SDL_joystick.h
SDL_log.h
SDL_MAX_UINT16
#define SDL_MAX_UINT16
An unsigned 16-bit integer type.
Definition: SDL_stdinc.h:189
SDL_CONTROLLER_BUTTON_RIGHTSTICK
Definition: SDL_gamecontroller.h:327
SDL_CONTROLLER_AXIS_LEFTX
Definition: SDL_gamecontroller.h:275
SDL_RELEASED
#define SDL_RELEASED
Definition: SDL_events.h:49
length
GLuint GLsizei GLsizei * length
Definition: SDL_opengl_glext.h:669
SDL_IsJoystickXboxOne
SDL_bool SDL_IsJoystickXboxOne(Uint16 vendor, Uint16 product)
Definition: SDL_joystick.c:1196
ctx
EGLContext ctx
Definition: eglext.h:208
SDL_CONTROLLER_BUTTON_B
Definition: SDL_gamecontroller.h:320
data
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1973
SDL_CONTROLLER_BUTTON_MAX
Definition: SDL_gamecontroller.h:334
SDL_PrivateJoystickAxis
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
Definition: SDL_joystick.c:833
factory
static NativeWindowFactory * factory
Definition: testnative.c:37
SDL_hidapijoystick_c.h
SDL_CONTROLLER_BUTTON_BACK
Definition: SDL_gamecontroller.h:323
SDL_CONTROLLER_BUTTON_LEFTSHOULDER
Definition: SDL_gamecontroller.h:328
SDL_PRESSED
#define SDL_PRESSED
Definition: SDL_events.h:50
Sint16
int16_t Sint16
Definition: SDL_stdinc.h:185
SDL_HIDAPI_DriverXbox360
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360
SDL_memcpy
#define SDL_memcpy
Definition: SDL_dynapi_overrides.h:387
Uint8
uint8_t Uint8
Definition: SDL_stdinc.h:179
SDL_GetHintBoolean
#define SDL_GetHintBoolean
Definition: SDL_dynapi_overrides.h:608
context
static screen_context_t context
Definition: video.c:25
SDL_HINT_XINPUT_ENABLED
#define SDL_HINT_XINPUT_ENABLED
A variable that lets you disable the detection and use of Xinput gamepad devices.
Definition: SDL_hints.h:428
SDL_HINT_JOYSTICK_HIDAPI_XBOX
#define SDL_HINT_JOYSTICK_HIDAPI_XBOX
A variable controlling whether the HIDAPI driver for XBox controllers should be used.
Definition: SDL_hints.h:566
HIDAPI_XboxControllerName
const char * HIDAPI_XboxControllerName(Uint16 vendor_id, Uint16 product_id)
SDL_CONTROLLER_AXIS_TRIGGERLEFT
Definition: SDL_gamecontroller.h:279
SDL_Log
#define SDL_Log
Definition: SDL_dynapi_overrides.h:31
WIN_CoUninitialize
void WIN_CoUninitialize(void)
SDL_free
#define SDL_free
Definition: SDL_dynapi_overrides.h:377
hid_read_timeout
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *device, unsigned char *data, size_t length, int milliseconds)
Read an Input report from a HID device with timeout.
SUCCEEDED
#define SUCCEEDED(x)
Definition: SDL_directx.h:51
SDL_IsJoystickXbox360
SDL_bool SDL_IsJoystickXbox360(Uint16 vendor, Uint16 product)
Definition: SDL_joystick.c:1183
SDL_FALSE
Definition: SDL_stdinc.h:163
SDL_JOYSTICK_POWER_WIRED
Definition: SDL_joystick.h:104
SDL_HIDAPI_DeviceDriver
Definition: SDL_hidapijoystick_c.h:46
SDL_PrivateJoystickButton
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
Definition: SDL_joystick.c:966
SDL_CONTROLLER_AXIS_MAX
Definition: SDL_gamecontroller.h:281
SDL_wcslen
#define SDL_wcslen
Definition: SDL_dynapi_overrides.h:390
SDL_GetTicks
Uint32 SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
SDL_min
#define SDL_min(x, y)
Definition: SDL_stdinc.h:406
SDL_gamecontroller.h
SDL_MIN_SINT16
#define SDL_MIN_SINT16
Definition: SDL_stdinc.h:184
SDL_CONTROLLER_BUTTON_START
Definition: SDL_gamecontroller.h:325
axis
SDL_Texture * axis
Definition: testgamecontroller.c:67
SDL_OutOfMemory
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
size
GLsizeiptr size
Definition: SDL_opengl_glext.h:537
WIN_CoInitialize
HRESULT WIN_CoInitialize(void)
SDL_calloc
#define SDL_calloc
Definition: SDL_dynapi_overrides.h:375
SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
Definition: SDL_gamecontroller.h:329
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:161
SDL_CONTROLLER_BUTTON_Y
Definition: SDL_gamecontroller.h:322
SDL_SetError
#define SDL_SetError
Definition: SDL_dynapi_overrides.h:30
SDL_TRUE
Definition: SDL_stdinc.h:164
SDL_hints.h
SDL_CONTROLLER_AXIS_LEFTY
Definition: SDL_gamecontroller.h:276
SDL_CONTROLLER_BUTTON_LEFTSTICK
Definition: SDL_gamecontroller.h:326
FAILED
#define FAILED(x)
Definition: SDL_directx.h:54
Uint32
uint32_t Uint32
Definition: SDL_stdinc.h:203
SDL_TICKS_PASSED
#define SDL_TICKS_PASSED(A, B)
Compare SDL ticks values, and return true if A has passed B.
Definition: SDL_timer.h:56
SDL_CONTROLLER_BUTTON_DPAD_DOWN
Definition: SDL_gamecontroller.h:331
SDL_CONTROLLER_BUTTON_DPAD_UP
Definition: SDL_gamecontroller.h:330
SDL_Unsupported
#define SDL_Unsupported()
Definition: SDL_error.h:53
string
GLsizei const GLchar *const * string
Definition: SDL_opengl_glext.h:688
SDL_CONTROLLER_AXIS_TRIGGERRIGHT
Definition: SDL_gamecontroller.h:280
hid_device
struct hid_device_ hid_device
Definition: hidapi.h:49
SDL_CONTROLLER_AXIS_RIGHTY
Definition: SDL_gamecontroller.h:278
SDL_CONTROLLER_BUTTON_X
Definition: SDL_gamecontroller.h:321
SDL_CONTROLLER_BUTTON_A
Definition: SDL_gamecontroller.h:319
state
struct xkb_state * state
Definition: SDL_waylandsym.h:113
i
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
SDL_CONTROLLER_BUTTON_DPAD_RIGHT
Definition: SDL_gamecontroller.h:333
Uint16
uint16_t Uint16
Definition: SDL_stdinc.h:191
SDL_CONTROLLER_BUTTON_GUIDE
Definition: SDL_gamecontroller.h:324