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