SDL  2.0
SDL_xinputjoystick.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2016 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 #include "../SDL_sysjoystick.h"
24 
25 #if SDL_JOYSTICK_XINPUT
26 
27 #include "SDL_assert.h"
28 #include "SDL_hints.h"
29 #include "SDL_windowsjoystick_c.h"
30 #include "SDL_xinputjoystick_c.h"
31 
32 /*
33  * Internal stuff.
34  */
35 static SDL_bool s_bXInputEnabled = SDL_TRUE;
36 
37 
38 static SDL_bool
39 SDL_XInputUseOldJoystickMapping()
40 {
41  static int s_XInputUseOldJoystickMapping = -1;
42  if (s_XInputUseOldJoystickMapping < 0) {
44  s_XInputUseOldJoystickMapping = (hint && *hint == '1') ? 1 : 0;
45  }
46  return (s_XInputUseOldJoystickMapping > 0);
47 }
48 
50 {
51  return s_bXInputEnabled;
52 }
53 
54 int
56 {
57  const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED);
58  if (env && !SDL_atoi(env)) {
59  s_bXInputEnabled = SDL_FALSE;
60  }
61 
62  if (s_bXInputEnabled && WIN_LoadXInputDLL() < 0) {
63  s_bXInputEnabled = SDL_FALSE; /* oh well. */
64  }
65  return 0;
66 }
67 
68 static char *
69 GetXInputName(const Uint8 userid, BYTE SubType)
70 {
71  char name[32];
72 
73  if (SDL_XInputUseOldJoystickMapping()) {
74  SDL_snprintf(name, sizeof(name), "X360 Controller #%u", 1 + userid);
75  } else {
76  switch (SubType) {
77  case XINPUT_DEVSUBTYPE_GAMEPAD:
78  SDL_snprintf(name, sizeof(name), "XInput Controller #%u", 1 + userid);
79  break;
80  case XINPUT_DEVSUBTYPE_WHEEL:
81  SDL_snprintf(name, sizeof(name), "XInput Wheel #%u", 1 + userid);
82  break;
83  case XINPUT_DEVSUBTYPE_ARCADE_STICK:
84  SDL_snprintf(name, sizeof(name), "XInput ArcadeStick #%u", 1 + userid);
85  break;
86  case XINPUT_DEVSUBTYPE_FLIGHT_STICK:
87  SDL_snprintf(name, sizeof(name), "XInput FlightStick #%u", 1 + userid);
88  break;
89  case XINPUT_DEVSUBTYPE_DANCE_PAD:
90  SDL_snprintf(name, sizeof(name), "XInput DancePad #%u", 1 + userid);
91  break;
92  case XINPUT_DEVSUBTYPE_GUITAR:
93  case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE:
94  case XINPUT_DEVSUBTYPE_GUITAR_BASS:
95  SDL_snprintf(name, sizeof(name), "XInput Guitar #%u", 1 + userid);
96  break;
97  case XINPUT_DEVSUBTYPE_DRUM_KIT:
98  SDL_snprintf(name, sizeof(name), "XInput DrumKit #%u", 1 + userid);
99  break;
100  case XINPUT_DEVSUBTYPE_ARCADE_PAD:
101  SDL_snprintf(name, sizeof(name), "XInput ArcadePad #%u", 1 + userid);
102  break;
103  default:
104  SDL_snprintf(name, sizeof(name), "XInput Device #%u", 1 + userid);
105  break;
106  }
107  }
108  return SDL_strdup(name);
109 }
110 
111 static void
112 AddXInputDevice(const Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext)
113 {
114  JoyStick_DeviceData *pPrevJoystick = NULL;
115  JoyStick_DeviceData *pNewJoystick = *pContext;
116 
117  if (SDL_XInputUseOldJoystickMapping() && SubType != XINPUT_DEVSUBTYPE_GAMEPAD)
118  return;
119 
120  if (SubType == XINPUT_DEVSUBTYPE_UNKNOWN)
121  return;
122 
123  while (pNewJoystick) {
124  if (pNewJoystick->bXInputDevice && (pNewJoystick->XInputUserId == userid) && (pNewJoystick->SubType == SubType)) {
125  /* if we are replacing the front of the list then update it */
126  if (pNewJoystick == *pContext) {
127  *pContext = pNewJoystick->pNext;
128  } else if (pPrevJoystick) {
129  pPrevJoystick->pNext = pNewJoystick->pNext;
130  }
131 
132  pNewJoystick->pNext = SYS_Joystick;
133  SYS_Joystick = pNewJoystick;
134  return; /* already in the list. */
135  }
136 
137  pPrevJoystick = pNewJoystick;
138  pNewJoystick = pNewJoystick->pNext;
139  }
140 
141  pNewJoystick = (JoyStick_DeviceData *)SDL_malloc(sizeof(JoyStick_DeviceData));
142  if (!pNewJoystick) {
143  return; /* better luck next time? */
144  }
145  SDL_zerop(pNewJoystick);
146 
147  pNewJoystick->joystickname = GetXInputName(userid, SubType);
148  if (!pNewJoystick->joystickname) {
149  SDL_free(pNewJoystick);
150  return; /* better luck next time? */
151  }
152 
153  pNewJoystick->bXInputDevice = SDL_TRUE;
154  if (SDL_XInputUseOldJoystickMapping()) {
155  SDL_zero(pNewJoystick->guid);
156  } else {
157  pNewJoystick->guid.data[0] = 'x';
158  pNewJoystick->guid.data[1] = 'i';
159  pNewJoystick->guid.data[2] = 'n';
160  pNewJoystick->guid.data[3] = 'p';
161  pNewJoystick->guid.data[4] = 'u';
162  pNewJoystick->guid.data[5] = 't';
163  pNewJoystick->guid.data[6] = SubType;
164  }
165  pNewJoystick->SubType = SubType;
166  pNewJoystick->XInputUserId = userid;
167  SDL_SYS_AddJoystickDevice(pNewJoystick);
168 }
169 
170 void
172 {
173  int iuserid;
174 
175  if (!s_bXInputEnabled) {
176  return;
177  }
178 
179  /* iterate in reverse, so these are in the final list in ascending numeric order. */
180  for (iuserid = XUSER_MAX_COUNT - 1; iuserid >= 0; iuserid--) {
181  const Uint8 userid = (Uint8)iuserid;
182  XINPUT_CAPABILITIES capabilities;
183  if (XINPUTGETCAPABILITIES(userid, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS) {
184  AddXInputDevice(userid, capabilities.SubType, pContext);
185  }
186  }
187 }
188 
189 int
190 SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
191 {
192  const Uint8 userId = joystickdevice->XInputUserId;
193  XINPUT_CAPABILITIES capabilities;
194  XINPUT_VIBRATION state;
195 
196  SDL_assert(s_bXInputEnabled);
197  SDL_assert(XINPUTGETCAPABILITIES);
198  SDL_assert(XINPUTSETSTATE);
199  SDL_assert(userId < XUSER_MAX_COUNT);
200 
201  joystick->hwdata->bXInputDevice = SDL_TRUE;
202 
203  if (XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities) != ERROR_SUCCESS) {
204  SDL_free(joystick->hwdata);
205  joystick->hwdata = NULL;
206  return SDL_SetError("Failed to obtain XInput device capabilities. Device disconnected?");
207  }
208  SDL_zero(state);
209  joystick->hwdata->bXInputHaptic = (XINPUTSETSTATE(userId, &state) == ERROR_SUCCESS);
210  joystick->hwdata->userid = userId;
211 
212  /* The XInput API has a hard coded button/axis mapping, so we just match it */
213  if (SDL_XInputUseOldJoystickMapping()) {
214  joystick->naxes = 6;
215  joystick->nbuttons = 15;
216  } else {
217  joystick->naxes = 6;
218  joystick->nbuttons = 11;
219  joystick->nhats = 1;
220  }
221  return 0;
222 }
223 
224 static void
225 UpdateXInputJoystickBatteryInformation(SDL_Joystick * joystick, XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation)
226 {
227  if ( pBatteryInformation->BatteryType != BATTERY_TYPE_UNKNOWN )
228  {
230  if (pBatteryInformation->BatteryType == BATTERY_TYPE_WIRED) {
231  ePowerLevel = SDL_JOYSTICK_POWER_WIRED;
232  } else {
233  switch ( pBatteryInformation->BatteryLevel )
234  {
235  case BATTERY_LEVEL_EMPTY:
236  ePowerLevel = SDL_JOYSTICK_POWER_EMPTY;
237  break;
238  case BATTERY_LEVEL_LOW:
239  ePowerLevel = SDL_JOYSTICK_POWER_LOW;
240  break;
241  case BATTERY_LEVEL_MEDIUM:
242  ePowerLevel = SDL_JOYSTICK_POWER_MEDIUM;
243  break;
244  default:
245  case BATTERY_LEVEL_FULL:
246  ePowerLevel = SDL_JOYSTICK_POWER_FULL;
247  break;
248  }
249  }
250 
251  SDL_PrivateJoystickBatteryLevel( joystick, ePowerLevel );
252  }
253 }
254 
255 static void
256 UpdateXInputJoystickState_OLD(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState, XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation)
257 {
258  static WORD s_XInputButtons[] = {
259  XINPUT_GAMEPAD_DPAD_UP, XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_RIGHT,
260  XINPUT_GAMEPAD_START, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB,
261  XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER,
262  XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y,
263  XINPUT_GAMEPAD_GUIDE
264  };
265  WORD wButtons = pXInputState->Gamepad.wButtons;
266  Uint8 button;
267 
268  SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX);
269  SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY)));
270  SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX);
271  SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY)));
272  SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768));
273  SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768));
274 
275  for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) {
276  SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED);
277  }
278 
279  UpdateXInputJoystickBatteryInformation( joystick, pBatteryInformation );
280 }
281 
282 static void
283 UpdateXInputJoystickState(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState, XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation)
284 {
285  static WORD s_XInputButtons[] = {
286  XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y,
287  XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_START,
288  XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB,
289  XINPUT_GAMEPAD_GUIDE
290  };
291  WORD wButtons = pXInputState->Gamepad.wButtons;
292  Uint8 button;
293  Uint8 hat = SDL_HAT_CENTERED;
294 
295  SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX);
296  SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY)));
297  SDL_PrivateJoystickAxis(joystick, 2, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768));
298  SDL_PrivateJoystickAxis(joystick, 3, (Sint16)pXInputState->Gamepad.sThumbRX);
299  SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY)));
300  SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768));
301 
302  for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) {
303  SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED);
304  }
305 
306  if (wButtons & XINPUT_GAMEPAD_DPAD_UP) {
307  hat |= SDL_HAT_UP;
308  }
309  if (wButtons & XINPUT_GAMEPAD_DPAD_DOWN) {
310  hat |= SDL_HAT_DOWN;
311  }
312  if (wButtons & XINPUT_GAMEPAD_DPAD_LEFT) {
313  hat |= SDL_HAT_LEFT;
314  }
315  if (wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) {
316  hat |= SDL_HAT_RIGHT;
317  }
318  SDL_PrivateJoystickHat(joystick, 0, hat);
319 
320  UpdateXInputJoystickBatteryInformation( joystick, pBatteryInformation );
321 }
322 
323 void
324 SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
325 {
326  HRESULT result;
327  XINPUT_STATE_EX XInputState;
328  XINPUT_BATTERY_INFORMATION_EX XBatteryInformation;
329 
330  if (!XINPUTGETSTATE)
331  return;
332 
333  result = XINPUTGETSTATE(joystick->hwdata->userid, &XInputState);
334  if (result == ERROR_DEVICE_NOT_CONNECTED) {
335  joystick->hwdata->send_remove_event = SDL_TRUE;
336  joystick->hwdata->removed = SDL_TRUE;
337  return;
338  }
339 
340  SDL_zero( XBatteryInformation );
341  if ( XINPUTGETBATTERYINFORMATION )
342  {
343  result = XINPUTGETBATTERYINFORMATION( joystick->hwdata->userid, BATTERY_DEVTYPE_GAMEPAD, &XBatteryInformation );
344  }
345 
346  /* only fire events if the data changed from last time */
347  if (XInputState.dwPacketNumber && XInputState.dwPacketNumber != joystick->hwdata->dwPacketNumber) {
348  if (SDL_XInputUseOldJoystickMapping()) {
349  UpdateXInputJoystickState_OLD(joystick, &XInputState, &XBatteryInformation);
350  } else {
351  UpdateXInputJoystickState(joystick, &XInputState, &XBatteryInformation);
352  }
353  joystick->hwdata->dwPacketNumber = XInputState.dwPacketNumber;
354  }
355 }
356 
357 void
358 SDL_XINPUT_JoystickClose(SDL_Joystick * joystick)
359 {
360 }
361 
362 void
364 {
365  if (s_bXInputEnabled) {
366  WIN_UnloadXInputDLL();
367  }
368 }
369 
370 SDL_bool
371 SDL_SYS_IsXInputGamepad_DeviceIndex(int device_index)
372 {
374  int index;
375 
376  for (index = device_index; index > 0; index--)
377  device = device->pNext;
378 
379  return (device->SubType == XINPUT_DEVSUBTYPE_GAMEPAD);
380 }
381 
382 #else /* !SDL_JOYSTICK_XINPUT */
383 
385 
387 {
388  return SDL_FALSE;
389 }
390 
391 int
393 {
394  return 0;
395 }
396 
397 void
399 {
400 }
401 
402 int
403 SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
404 {
405  return SDL_Unsupported();
406 }
407 
408 void
409 SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
410 {
411 }
412 
413 void
414 SDL_XINPUT_JoystickClose(SDL_Joystick * joystick)
415 {
416 }
417 
418 void
420 {
421 }
422 
423 #endif /* SDL_JOYSTICK_XINPUT */
424 
425 /* vi: set ts=4 sw=4 expandtab: */
JoyStick_DeviceData * SYS_Joystick
SDL_Texture * button
GLuint64EXT * result
int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
Definition: SDL_joystick.c:542
struct JoyStick_DeviceData * pNext
void SDL_SYS_AddJoystickDevice(JoyStick_DeviceData *device)
struct xkb_state * state
#define SDL_GetHint
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
Definition: SDL_joystick.c:618
GLuint const GLchar * name
int SDL_XINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice)
Uint8 data[16]
Definition: SDL_joystick.h:69
#define SDL_zerop(x)
Definition: SDL_stdinc.h:356
void SDL_XINPUT_JoystickClose(SDL_Joystick *joystick)
#define SDL_max(x, y)
Definition: SDL_stdinc.h:346
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
Definition: SDL_joystick.c:501
#define SDL_HINT_XINPUT_ENABLED
A variable that lets you disable the detection and use of Xinput gamepad devices. ...
Definition: SDL_hints.h:299
SDL_bool
Definition: SDL_stdinc.h:126
#define SDL_HAT_RIGHT
Definition: SDL_joystick.h:209
#define SDL_HAT_LEFT
Definition: SDL_joystick.h:211
uint8_t Uint8
An unsigned 8-bit integer type.
Definition: SDL_stdinc.h:139
void SDL_free(void *mem)
SDL_bool SDL_XINPUT_Enabled(void)
#define SDL_zero(x)
Definition: SDL_stdinc.h:355
#define SDL_atoi
SDL_JoystickPowerLevel
Definition: SDL_joystick.h:74
GLuint index
int SDL_XINPUT_JoystickInit(void)
#define SDL_assert(condition)
Definition: SDL_assert.h:167
#define NULL
Definition: begin_code.h:143
#define SDL_SetError
void SDL_XINPUT_JoystickQuit(void)
#define SDL_HINT_XINPUT_USE_OLD_JOYSTICK_MAPPING
A variable that causes SDL to use the old axis and button mapping for XInput devices.
Definition: SDL_hints.h:309
#define SDL_strdup
void SDL_PrivateJoystickBatteryLevel(SDL_Joystick *joystick, SDL_JoystickPowerLevel ePowerLevel)
Definition: SDL_joystick.c:847
#define SDL_snprintf
void SDL_XINPUT_JoystickUpdate(SDL_Joystick *joystick)
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:93
#define SDL_malloc
#define SDL_PRESSED
Definition: SDL_events.h:50
#define SDL_HAT_CENTERED
Definition: SDL_joystick.h:207
#define SDL_RELEASED
Definition: SDL_events.h:49
#define SDL_HAT_UP
Definition: SDL_joystick.h:208
#define SDL_HAT_DOWN
Definition: SDL_joystick.h:210
#define SDL_Unsupported()
Definition: SDL_error.h:53
void SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
int16_t Sint16
A signed 16-bit integer type.
Definition: SDL_stdinc.h:143