SDL_xinputjoystick.c
Go to the documentation of this file.
/*
Simple DirectMedia Layer
Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#include "../SDL_sysjoystick.h"
#if SDL_JOYSTICK_XINPUT
#include "SDL_assert.h"
#include "SDL_hints.h"
#include "SDL_windowsjoystick_c.h"
#include "SDL_xinputjoystick_c.h"
/*
* Internal stuff.
*/
static SDL_bool s_bXInputEnabled = SDL_TRUE;
static SDL_bool
SDL_XInputUseOldJoystickMapping()
{
static int s_XInputUseOldJoystickMapping = -1;
if (s_XInputUseOldJoystickMapping < 0) {
const char *hint = SDL_GetHint(SDL_HINT_XINPUT_USE_OLD_JOYSTICK_MAPPING);
s_XInputUseOldJoystickMapping = (hint && *hint == '1') ? 1 : 0;
}
return (s_XInputUseOldJoystickMapping > 0);
}
SDL_bool SDL_XINPUT_Enabled(void)
{
return s_bXInputEnabled;
}
int
SDL_XINPUT_JoystickInit(void)
{
const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED);
if (env && !SDL_atoi(env)) {
s_bXInputEnabled = SDL_FALSE;
}
if (s_bXInputEnabled && WIN_LoadXInputDLL() < 0) {
s_bXInputEnabled = SDL_FALSE; /* oh well. */
}
return 0;
}
static char *
GetXInputName(const Uint8 userid, BYTE SubType)
{
char name[32];
if (SDL_XInputUseOldJoystickMapping()) {
SDL_snprintf(name, sizeof(name), "X360 Controller #%u", 1 + userid);
} else {
switch (SubType) {
case XINPUT_DEVSUBTYPE_GAMEPAD:
SDL_snprintf(name, sizeof(name), "XInput Controller #%u", 1 + userid);
break;
case XINPUT_DEVSUBTYPE_WHEEL:
SDL_snprintf(name, sizeof(name), "XInput Wheel #%u", 1 + userid);
break;
case XINPUT_DEVSUBTYPE_ARCADE_STICK:
SDL_snprintf(name, sizeof(name), "XInput ArcadeStick #%u", 1 + userid);
break;
case XINPUT_DEVSUBTYPE_FLIGHT_STICK:
SDL_snprintf(name, sizeof(name), "XInput FlightStick #%u", 1 + userid);
break;
case XINPUT_DEVSUBTYPE_DANCE_PAD:
SDL_snprintf(name, sizeof(name), "XInput DancePad #%u", 1 + userid);
break;
case XINPUT_DEVSUBTYPE_GUITAR:
case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE:
case XINPUT_DEVSUBTYPE_GUITAR_BASS:
SDL_snprintf(name, sizeof(name), "XInput Guitar #%u", 1 + userid);
break;
case XINPUT_DEVSUBTYPE_DRUM_KIT:
SDL_snprintf(name, sizeof(name), "XInput DrumKit #%u", 1 + userid);
break;
case XINPUT_DEVSUBTYPE_ARCADE_PAD:
SDL_snprintf(name, sizeof(name), "XInput ArcadePad #%u", 1 + userid);
break;
default:
SDL_snprintf(name, sizeof(name), "XInput Device #%u", 1 + userid);
break;
}
}
return SDL_strdup(name);
}
static void
AddXInputDevice(const Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext)
{
JoyStick_DeviceData *pPrevJoystick = NULL;
JoyStick_DeviceData *pNewJoystick = *pContext;
if (SDL_XInputUseOldJoystickMapping() && SubType != XINPUT_DEVSUBTYPE_GAMEPAD)
return;
if (SubType == XINPUT_DEVSUBTYPE_UNKNOWN)
return;
while (pNewJoystick) {
if (pNewJoystick->bXInputDevice && (pNewJoystick->XInputUserId == userid) && (pNewJoystick->SubType == SubType)) {
/* if we are replacing the front of the list then update it */
if (pNewJoystick == *pContext) {
*pContext = pNewJoystick->pNext;
} else if (pPrevJoystick) {
pPrevJoystick->pNext = pNewJoystick->pNext;
}
pNewJoystick->pNext = SYS_Joystick;
SYS_Joystick = pNewJoystick;
return; /* already in the list. */
}
pPrevJoystick = pNewJoystick;
pNewJoystick = pNewJoystick->pNext;
}
pNewJoystick = (JoyStick_DeviceData *)SDL_malloc(sizeof(JoyStick_DeviceData));
if (!pNewJoystick) {
return; /* better luck next time? */
}
SDL_zerop(pNewJoystick);
pNewJoystick->joystickname = GetXInputName(userid, SubType);
if (!pNewJoystick->joystickname) {
SDL_free(pNewJoystick);
return; /* better luck next time? */
}
pNewJoystick->bXInputDevice = SDL_TRUE;
if (SDL_XInputUseOldJoystickMapping()) {
SDL_zero(pNewJoystick->guid);
} else {
pNewJoystick->guid.data[0] = 'x';
pNewJoystick->guid.data[1] = 'i';
pNewJoystick->guid.data[2] = 'n';
pNewJoystick->guid.data[3] = 'p';
pNewJoystick->guid.data[4] = 'u';
pNewJoystick->guid.data[5] = 't';
pNewJoystick->guid.data[6] = SubType;
}
pNewJoystick->SubType = SubType;
pNewJoystick->XInputUserId = userid;
SDL_SYS_AddJoystickDevice(pNewJoystick);
}
void
SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
{
int iuserid;
if (!s_bXInputEnabled) {
return;
}
/* iterate in reverse, so these are in the final list in ascending numeric order. */
for (iuserid = XUSER_MAX_COUNT - 1; iuserid >= 0; iuserid--) {
const Uint8 userid = (Uint8)iuserid;
XINPUT_CAPABILITIES capabilities;
if (XINPUTGETCAPABILITIES(userid, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS) {
AddXInputDevice(userid, capabilities.SubType, pContext);
}
}
}
int
SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
{
const Uint8 userId = joystickdevice->XInputUserId;
XINPUT_CAPABILITIES capabilities;
XINPUT_VIBRATION state;
SDL_assert(s_bXInputEnabled);
SDL_assert(XINPUTGETCAPABILITIES);
SDL_assert(XINPUTSETSTATE);
SDL_assert(userId < XUSER_MAX_COUNT);
joystick->hwdata->bXInputDevice = SDL_TRUE;
if (XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities) != ERROR_SUCCESS) {
SDL_free(joystick->hwdata);
joystick->hwdata = NULL;
return SDL_SetError("Failed to obtain XInput device capabilities. Device disconnected?");
}
SDL_zero(state);
joystick->hwdata->bXInputHaptic = (XINPUTSETSTATE(userId, &state) == ERROR_SUCCESS);
joystick->hwdata->userid = userId;
/* The XInput API has a hard coded button/axis mapping, so we just match it */
if (SDL_XInputUseOldJoystickMapping()) {
joystick->naxes = 6;
joystick->nbuttons = 15;
} else {
joystick->naxes = 6;
joystick->nbuttons = 11;
joystick->nhats = 1;
}
return 0;
}
static void
UpdateXInputJoystickBatteryInformation(SDL_Joystick * joystick, XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation)
{
if ( pBatteryInformation->BatteryType != BATTERY_TYPE_UNKNOWN )
{
SDL_JoystickPowerLevel ePowerLevel = SDL_JOYSTICK_POWER_UNKNOWN;
if (pBatteryInformation->BatteryType == BATTERY_TYPE_WIRED) {
ePowerLevel = SDL_JOYSTICK_POWER_WIRED;
} else {
switch ( pBatteryInformation->BatteryLevel )
{
case BATTERY_LEVEL_EMPTY:
ePowerLevel = SDL_JOYSTICK_POWER_EMPTY;
break;
case BATTERY_LEVEL_LOW:
ePowerLevel = SDL_JOYSTICK_POWER_LOW;
break;
case BATTERY_LEVEL_MEDIUM:
ePowerLevel = SDL_JOYSTICK_POWER_MEDIUM;
break;
default:
case BATTERY_LEVEL_FULL:
ePowerLevel = SDL_JOYSTICK_POWER_FULL;
break;
}
}
SDL_PrivateJoystickBatteryLevel( joystick, ePowerLevel );
}
}
static void
UpdateXInputJoystickState_OLD(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState, XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation)
{
static WORD s_XInputButtons[] = {
XINPUT_GAMEPAD_DPAD_UP, XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_RIGHT,
XINPUT_GAMEPAD_START, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB,
XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER,
XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y,
XINPUT_GAMEPAD_GUIDE
};
WORD wButtons = pXInputState->Gamepad.wButtons;
Uint8 button;
SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX);
SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY)));
SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX);
SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY)));
SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768));
SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768));
for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) {
SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED);
}
UpdateXInputJoystickBatteryInformation( joystick, pBatteryInformation );
}
static void
UpdateXInputJoystickState(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState, XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation)
{
static WORD s_XInputButtons[] = {
XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y,
XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_START,
XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB,
XINPUT_GAMEPAD_GUIDE
};
WORD wButtons = pXInputState->Gamepad.wButtons;
Uint8 button;
Uint8 hat = SDL_HAT_CENTERED;
SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX);
SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY)));
SDL_PrivateJoystickAxis(joystick, 2, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768));
SDL_PrivateJoystickAxis(joystick, 3, (Sint16)pXInputState->Gamepad.sThumbRX);
SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY)));
SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768));
for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) {
SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED);
}
if (wButtons & XINPUT_GAMEPAD_DPAD_UP) {
hat |= SDL_HAT_UP;
}
if (wButtons & XINPUT_GAMEPAD_DPAD_DOWN) {
hat |= SDL_HAT_DOWN;
}
if (wButtons & XINPUT_GAMEPAD_DPAD_LEFT) {
hat |= SDL_HAT_LEFT;
}
if (wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) {
hat |= SDL_HAT_RIGHT;
}
SDL_PrivateJoystickHat(joystick, 0, hat);
UpdateXInputJoystickBatteryInformation( joystick, pBatteryInformation );
}
void
SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
{
HRESULT result;
XINPUT_STATE_EX XInputState;
XINPUT_BATTERY_INFORMATION_EX XBatteryInformation;
if (!XINPUTGETSTATE)
return;
result = XINPUTGETSTATE(joystick->hwdata->userid, &XInputState);
if (result == ERROR_DEVICE_NOT_CONNECTED) {
joystick->hwdata->send_remove_event = SDL_TRUE;
joystick->hwdata->removed = SDL_TRUE;
return;
}
SDL_zero( XBatteryInformation );
if ( XINPUTGETBATTERYINFORMATION )
{
result = XINPUTGETBATTERYINFORMATION( joystick->hwdata->userid, BATTERY_DEVTYPE_GAMEPAD, &XBatteryInformation );
}
/* only fire events if the data changed from last time */
if (XInputState.dwPacketNumber && XInputState.dwPacketNumber != joystick->hwdata->dwPacketNumber) {
if (SDL_XInputUseOldJoystickMapping()) {
UpdateXInputJoystickState_OLD(joystick, &XInputState, &XBatteryInformation);
} else {
UpdateXInputJoystickState(joystick, &XInputState, &XBatteryInformation);
}
joystick->hwdata->dwPacketNumber = XInputState.dwPacketNumber;
}
}
void
SDL_XINPUT_JoystickClose(SDL_Joystick * joystick)
{
}
void
SDL_XINPUT_JoystickQuit(void)
{
if (s_bXInputEnabled) {
WIN_UnloadXInputDLL();
}
}
SDL_bool
SDL_SYS_IsXInputGamepad_DeviceIndex(int device_index)
{
JoyStick_DeviceData *device = SYS_Joystick;
int index;
for (index = device_index; index > 0; index--)
device = device->pNext;
return (device->SubType == XINPUT_DEVSUBTYPE_GAMEPAD);
}
#else /* !SDL_JOYSTICK_XINPUT */
typedef struct JoyStick_DeviceData JoyStick_DeviceData;
SDL_bool SDL_XINPUT_Enabled(void)
{
return SDL_FALSE;
}
int
SDL_XINPUT_JoystickInit(void)
{
return 0;
}
void
SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
{
}
int
SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
{
return SDL_Unsupported();
}
void
SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
{
}
void
SDL_XINPUT_JoystickClose(SDL_Joystick * joystick)
{
}
void
SDL_XINPUT_JoystickQuit(void)
{
}
#endif /* SDL_JOYSTICK_XINPUT */
/* vi: set ts=4 sw=4 expandtab: */
SYS_Joystick
JoyStick_DeviceData * SYS_Joystick
JoyStick_DeviceData::guid
SDL_JoystickGUID guid
Definition: SDL_windowsjoystick_c.h:32
button
SDL_Texture * button
Definition: testgamecontroller.c:67
result
GLuint64EXT * result
Definition: SDL_opengl_glext.h:9432
SDL_JOYSTICK_POWER_LOW
Definition: SDL_joystick.h:78
SDL_PrivateJoystickHat
int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
Definition: SDL_joystick.c:542
SDL_JOYSTICK_POWER_EMPTY
Definition: SDL_joystick.h:77
JoyStick_DeviceData::SubType
BYTE SubType
Definition: SDL_windowsjoystick_c.h:37
JoyStick_DeviceData::pNext
struct JoyStick_DeviceData * pNext
Definition: SDL_windowsjoystick_c.h:40
SDL_SYS_AddJoystickDevice
void SDL_SYS_AddJoystickDevice(JoyStick_DeviceData *device)
JoyStick_DeviceData
Definition: SDL_windowsjoystick_c.h:30
state
struct xkb_state * state
Definition: SDL_waylandsym.h:97
JoyStick_DeviceData::bXInputDevice
SDL_bool bXInputDevice
Definition: SDL_windowsjoystick_c.h:36
SDL_GetHint
#define SDL_GetHint
Definition: SDL_dynapi_overrides.h:197
SDL_PrivateJoystickButton
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
Definition: SDL_joystick.c:618
JoyStick_DeviceData::joystickname
char * joystickname
Definition: SDL_windowsjoystick_c.h:33
name
GLuint const GLchar * name
Definition: SDL_opengl_glext.h:660
SDL_XINPUT_JoystickOpen
int SDL_XINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice)
Definition: SDL_xinputjoystick.c:403
SDL_JoystickGUID::data
Uint8 data[16]
Definition: SDL_joystick.h:69
SDL_zerop
#define SDL_zerop(x)
Definition: SDL_stdinc.h:358
SDL_XINPUT_JoystickClose
void SDL_XINPUT_JoystickClose(SDL_Joystick *joystick)
Definition: SDL_xinputjoystick.c:414
SDL_max
#define SDL_max(x, y)
Definition: SDL_stdinc.h:348
SDL_PrivateJoystickAxis
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
Definition: SDL_joystick.c:501
SDL_xinputjoystick_c.h
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:299
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:126
SDL_HAT_RIGHT
#define SDL_HAT_RIGHT
Definition: SDL_joystick.h:209
SDL_HAT_LEFT
#define SDL_HAT_LEFT
Definition: SDL_joystick.h:211
Uint8
uint8_t Uint8
An unsigned 8-bit integer type.
Definition: SDL_stdinc.h:139
SDL_free
void SDL_free(void *mem)
SDL_windowsjoystick_c.h
SDL_hints.h
SDL_XINPUT_Enabled
SDL_bool SDL_XINPUT_Enabled(void)
Definition: SDL_xinputjoystick.c:386
SDL_zero
#define SDL_zero(x)
Definition: SDL_stdinc.h:357
SDL_JOYSTICK_POWER_UNKNOWN
Definition: SDL_joystick.h:76
SDL_TRUE
Definition: SDL_stdinc.h:129
SDL_atoi
#define SDL_atoi
Definition: SDL_dynapi_overrides.h:416
SDL_JoystickPowerLevel
SDL_JoystickPowerLevel
Definition: SDL_joystick.h:74
index
GLuint index
Definition: SDL_opengl_glext.h:660
SDL_XINPUT_JoystickInit
int SDL_XINPUT_JoystickInit(void)
Definition: SDL_xinputjoystick.c:392
SDL_JOYSTICK_POWER_WIRED
Definition: SDL_joystick.h:81
SDL_assert
#define SDL_assert(condition)
Definition: SDL_assert.h:167
NULL
#define NULL
Definition: begin_code.h:143
JoyStick_DeviceData::XInputUserId
Uint8 XInputUserId
Definition: SDL_windowsjoystick_c.h:38
SDL_SetError
#define SDL_SetError
Definition: SDL_dynapi_overrides.h:36
SDL_XINPUT_JoystickQuit
void SDL_XINPUT_JoystickQuit(void)
Definition: SDL_xinputjoystick.c:419
SDL_HINT_XINPUT_USE_OLD_JOYSTICK_MAPPING
#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
SDL_JOYSTICK_POWER_MEDIUM
Definition: SDL_joystick.h:79
SDL_strdup
#define SDL_strdup
Definition: SDL_dynapi_overrides.h:403
SDL_PrivateJoystickBatteryLevel
void SDL_PrivateJoystickBatteryLevel(SDL_Joystick *joystick, SDL_JoystickPowerLevel ePowerLevel)
Definition: SDL_joystick.c:847
SDL_FALSE
Definition: SDL_stdinc.h:128
SDL_snprintf
#define SDL_snprintf
Definition: SDL_dynapi_overrides.h:46
SDL_XINPUT_JoystickUpdate
void SDL_XINPUT_JoystickUpdate(SDL_Joystick *joystick)
Definition: SDL_xinputjoystick.c:409
SDL_arraysize
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:93
SDL_malloc
#define SDL_malloc
Definition: SDL_dynapi_overrides.h:380
SDL_PRESSED
#define SDL_PRESSED
Definition: SDL_events.h:50
SDL_HAT_CENTERED
#define SDL_HAT_CENTERED
Definition: SDL_joystick.h:207
SDL_RELEASED
#define SDL_RELEASED
Definition: SDL_events.h:49
SDL_HAT_UP
#define SDL_HAT_UP
Definition: SDL_joystick.h:208
SDL_HAT_DOWN
#define SDL_HAT_DOWN
Definition: SDL_joystick.h:210
SDL_JOYSTICK_POWER_FULL
Definition: SDL_joystick.h:80
SDL_Unsupported
#define SDL_Unsupported()
Definition: SDL_error.h:53
SDL_XINPUT_JoystickDetect
void SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
Definition: SDL_xinputjoystick.c:398
Sint16
int16_t Sint16
A signed 16-bit integer type.
Definition: SDL_stdinc.h:143
SDL_assert.h