Skip to content

Commit

Permalink
Added SDL hints to filter the set of game controllers reported by SDL
Browse files Browse the repository at this point in the history
  • Loading branch information
slouken committed Aug 9, 2017
1 parent dc40018 commit c49fa37
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 36 deletions.
32 changes: 26 additions & 6 deletions include/SDL_hints.h
Expand Up @@ -359,7 +359,6 @@ extern "C" {
*/
#define SDL_HINT_ACCELEROMETER_AS_JOYSTICK "SDL_ACCELEROMETER_AS_JOYSTICK"


/**
* \brief A variable that lets you disable the detection and use of Xinput gamepad devices
*
Expand All @@ -369,7 +368,6 @@ extern "C" {
*/
#define SDL_HINT_XINPUT_ENABLED "SDL_XINPUT_ENABLED"


/**
* \brief A variable that causes SDL to use the old axis and button mapping for XInput devices.
*
Expand All @@ -379,9 +377,8 @@ extern "C" {
*/
#define SDL_HINT_XINPUT_USE_OLD_JOYSTICK_MAPPING "SDL_XINPUT_USE_OLD_JOYSTICK_MAPPING"


/**
* \brief A variable that lets you manually hint extra gamecontroller db entries
* \brief A variable that lets you manually hint extra gamecontroller db entries.
*
* The variable should be newline delimited rows of gamecontroller config data, see SDL_gamecontroller.h
*
Expand All @@ -390,6 +387,31 @@ extern "C" {
*/
#define SDL_HINT_GAMECONTROLLERCONFIG "SDL_GAMECONTROLLERCONFIG"

/**
* \brief A variable containing a list of devices to skip when scanning for game controllers.
*
* The format of the string is a comma separated list of USB VID/PID pairs
* in hexadecimal form, e.g.
*
* 0xAAAA/0xBBBB,0xCCCC/0xDDDD
*
* The variable can also take the form of @file, in which case the named
* file will be loaded and interpreted as the value of the variable.
*/
#define SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES "SDL_GAMECONTROLLER_IGNORE_DEVICES"

/**
* \brief If set, all devices will be skipped when scanning for game controllers except for the ones listed in this variable.
*
* The format of the string is a comma separated list of USB VID/PID pairs
* in hexadecimal form, e.g.
*
* 0xAAAA/0xBBBB,0xCCCC/0xDDDD
*
* The variable can also take the form of @file, in which case the named
* file will be loaded and interpreted as the value of the variable.
*/
#define SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT "SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT"

/**
* \brief A variable that lets you enable joystick (and gamecontroller) events even when your app is in the background.
Expand All @@ -404,7 +426,6 @@ extern "C" {
*/
#define SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS "SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS"


/**
* \brief If set to "0" then never set the top most bit on a SDL Window, even if the video mode expects it.
* This is a debugging aid for developers and not expected to be used by end users. The default is "1"
Expand All @@ -415,7 +436,6 @@ extern "C" {
*/
#define SDL_HINT_ALLOW_TOPMOST "SDL_ALLOW_TOPMOST"


/**
* \brief A variable that controls the timer resolution, in milliseconds.
*
Expand Down
203 changes: 175 additions & 28 deletions src/joystick/SDL_gamecontroller.c
Expand Up @@ -112,6 +112,74 @@ struct _SDL_GameController
};


typedef struct
{
int num_entries;
int max_entries;
Uint32 *entries;
} SDL_vidpid_list;

static SDL_vidpid_list SDL_allowed_controllers;
static SDL_vidpid_list SDL_ignored_controllers;

static void
SDL_LoadVIDPIDListFromHint(const char *hint, SDL_vidpid_list *list)
{
Uint32 entry;
char *spot;
char *file = NULL;

list->num_entries = 0;

if (hint && *hint == '@') {
spot = file = (char *)SDL_LoadFile(hint+1, NULL);
} else {
spot = (char *)hint;
}

if (!spot) {
return;
}

while ((spot = SDL_strstr(spot, "0x")) != NULL) {
entry = SDL_strtol(spot, &spot, 0);
entry <<= 16;
spot = SDL_strstr(spot, "0x");
if (!spot) {
break;
}
entry |= SDL_strtol(spot, &spot, 0);

if (list->num_entries == list->max_entries) {
int max_entries = list->max_entries + 16;
Uint32 *entries = (Uint32 *)SDL_realloc(list->entries, max_entries*sizeof(*list->entries));
if (entries == NULL) {
/* Out of memory, go with what we have already */
break;
}
list->entries = entries;
list->max_entries = max_entries;
}
list->entries[list->num_entries++] = entry;
}

if (file) {
SDL_free(file);
}
}

static void
SDL_GameControllerIgnoreDevicesChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
{
SDL_LoadVIDPIDListFromHint(hint, &SDL_ignored_controllers);
}

static void
SDL_GameControllerIgnoreDevicesExceptChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
{
SDL_LoadVIDPIDListFromHint(hint, &SDL_allowed_controllers);
}

static int SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value);
static int SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state);

Expand Down Expand Up @@ -799,50 +867,51 @@ SDL_PrivateAddMappingForGUID(SDL_JoystickGUID jGUID, const char *mappingString,
/*
* Helper function to determine pre-calculated offset to certain joystick mappings
*/
static ControllerMapping_t *SDL_PrivateGetControllerMapping(int device_index)
static ControllerMapping_t *SDL_PrivateGetControllerMappingForNameAndGUID(const char *name, SDL_JoystickGUID guid)
{
SDL_JoystickGUID jGUID = SDL_JoystickGetDeviceGUID(device_index);
ControllerMapping_t *mapping;

(void) s_pEmscriptenMapping; /* pacify ARMCC */

mapping = SDL_PrivateGetControllerMappingForGUID(&jGUID);
#if SDL_JOYSTICK_XINPUT
if (!mapping && SDL_SYS_IsXInputGamepad_DeviceIndex(device_index)) {
mapping = s_pXInputMapping;
}
#endif
mapping = SDL_PrivateGetControllerMappingForGUID(&guid);
#if defined(SDL_JOYSTICK_EMSCRIPTEN)
if (!mapping && s_pEmscriptenMapping) {
mapping = s_pEmscriptenMapping;
}
#else
(void) s_pEmscriptenMapping; /* pacify ARMCC */
#endif
#ifdef __LINUX__
if (!mapping) {
const char *name = SDL_JoystickNameForIndex(device_index);
if (name) {
if (SDL_strstr(name, "Xbox 360 Wireless Receiver")) {
/* The Linux driver xpad.c maps the wireless dpad to buttons */
SDL_bool existing;
mapping = SDL_PrivateAddMappingForGUID(jGUID,
if (!mapping && name) {
if (SDL_strstr(name, "Xbox 360 Wireless Receiver")) {
/* The Linux driver xpad.c maps the wireless dpad to buttons */
SDL_bool existing;
mapping = SDL_PrivateAddMappingForGUID(jGUID,
"none,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
&existing, SDL_CONTROLLER_MAPPING_PRIORITY_DEFAULT);
}
&existing, SDL_CONTROLLER_MAPPING_PRIORITY_DEFAULT);
}
}
#endif /* __LINUX__ */

if (!mapping) {
const char *name = SDL_JoystickNameForIndex(device_index);
if (name) {
if (SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box")) {
mapping = s_pXInputMapping;
}
if (!mapping && name) {
if (SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box")) {
mapping = s_pXInputMapping;
}
}
return mapping;
}

static ControllerMapping_t *SDL_PrivateGetControllerMapping(int device_index)
{
const char *name = SDL_JoystickNameForIndex(device_index);
SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index);
ControllerMapping_t *mapping = SDL_PrivateGetControllerMappingForNameAndGUID(name, guid);
#if SDL_JOYSTICK_XINPUT
if (!mapping && SDL_SYS_IsXInputGamepad_DeviceIndex(device_index)) {
mapping = s_pXInputMapping;
}
#endif
return mapping;
}

/*
* Add or update an entry into the Mappings Database
*/
Expand Down Expand Up @@ -1092,7 +1161,7 @@ SDL_GameControllerLoadHints()
* Initialize the game controller system, mostly load our DB of controller config mappings
*/
int
SDL_GameControllerInit(void)
SDL_GameControllerInitMappings(void)
{
int i = 0;
const char *pMappingString = NULL;
Expand All @@ -1107,6 +1176,19 @@ SDL_GameControllerInit(void)
/* load in any user supplied config */
SDL_GameControllerLoadHints();

SDL_AddHintCallback(SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES,
SDL_GameControllerIgnoreDevicesChanged, NULL);
SDL_AddHintCallback(SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT,
SDL_GameControllerIgnoreDevicesExceptChanged, NULL);

return (0);
}

int
SDL_GameControllerInit(void)
{
int i;

/* watch for joy events and fire controller ones if needed */
SDL_AddEventWatch(SDL_GameControllerEventWatcher, NULL);

Expand Down Expand Up @@ -1138,6 +1220,19 @@ SDL_GameControllerNameForIndex(int device_index)
}


/*
* Return 1 if the joystick with this name and GUID is a supported controller
*/
SDL_bool
SDL_IsGameControllerNameAndGUID(const char *name, SDL_JoystickGUID guid)
{
ControllerMapping_t *pSupportedController = SDL_PrivateGetControllerMappingForNameAndGUID(name, guid);
if (pSupportedController) {
return SDL_TRUE;
}
return SDL_FALSE;
}

/*
* Return 1 if the joystick at this device index is a supported controller
*/
Expand All @@ -1151,6 +1246,41 @@ SDL_IsGameController(int device_index)
return SDL_FALSE;
}

/*
* Return 1 if the game controller should be ignored by SDL
*/
SDL_bool SDL_ShouldIgnoreGameController(const char *name, SDL_JoystickGUID guid)
{
int i;
Uint16 vendor;
Uint16 product;
Uint32 vidpid;

if (SDL_allowed_controllers.num_entries == 0 &&
SDL_ignored_controllers.num_entries == 0) {
return SDL_FALSE;
}

SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL);
vidpid = MAKE_VIDPID(vendor, product);

if (SDL_allowed_controllers.num_entries > 0) {
for (i = 0; i < SDL_allowed_controllers.num_entries; ++i) {
if (vidpid == SDL_allowed_controllers.entries[i]) {
return SDL_FALSE;
}
}
return SDL_TRUE;
} else {
for (i = 0; i < SDL_ignored_controllers.num_entries; ++i) {
if (vidpid == SDL_ignored_controllers.entries[i]) {
return SDL_TRUE;
}
}
return SDL_FALSE;
}
}

/*
* Open a controller for use - the index passed as an argument refers to
* the N'th controller on the system. This index is the value which will
Expand Down Expand Up @@ -1536,14 +1666,18 @@ SDL_GameControllerClose(SDL_GameController * gamecontroller)
void
SDL_GameControllerQuit(void)
{
ControllerMapping_t *pControllerMap;

SDL_LockJoystickList();
while (SDL_gamecontrollers) {
SDL_gamecontrollers->ref_count = 1;
SDL_GameControllerClose(SDL_gamecontrollers);
}
SDL_UnlockJoystickList();
}

void
SDL_GameControllerQuitMappings(void)
{
ControllerMapping_t *pControllerMap;

while (s_pSupportedControllers) {
pControllerMap = s_pSupportedControllers;
Expand All @@ -1555,6 +1689,19 @@ SDL_GameControllerQuit(void)

SDL_DelEventWatch(SDL_GameControllerEventWatcher, NULL);

SDL_DelHintCallback(SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES,
SDL_GameControllerIgnoreDevicesChanged, NULL);
SDL_DelHintCallback(SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT,
SDL_GameControllerIgnoreDevicesExceptChanged, NULL);

if (SDL_allowed_controllers.entries) {
SDL_free(SDL_allowed_controllers.entries);
SDL_zero(SDL_allowed_controllers);
}
if (SDL_ignored_controllers.entries) {
SDL_free(SDL_ignored_controllers.entries);
SDL_zero(SDL_ignored_controllers);
}
}

/*
Expand Down
10 changes: 8 additions & 2 deletions src/joystick/SDL_joystick.c
Expand Up @@ -70,6 +70,8 @@ SDL_JoystickInit(void)
{
int status;

SDL_GameControllerInitMappings();

/* Create the joystick list lock */
if (!SDL_joystick_lock) {
SDL_joystick_lock = SDL_CreateMutex();
Expand Down Expand Up @@ -561,10 +563,15 @@ SDL_JoystickQuit(void)
SDL_QuitSubSystem(SDL_INIT_EVENTS);
#endif

SDL_DelHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
SDL_JoystickAllowBackgroundEventsChanged, NULL);

if (SDL_joystick_lock) {
SDL_DestroyMutex(SDL_joystick_lock);
SDL_joystick_lock = NULL;
}

SDL_GameControllerQuitMappings();
}


Expand Down Expand Up @@ -930,7 +937,7 @@ SDL_JoystickEventState(int state)
#endif /* SDL_EVENTS_DISABLED */
}

static void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version)
void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version)
{
Uint16 *guid16 = (Uint16 *)guid.data;

Expand Down Expand Up @@ -1273,5 +1280,4 @@ SDL_JoystickPowerLevel SDL_JoystickCurrentPowerLevel(SDL_Joystick * joystick)
return joystick->epowerlevel;
}


/* vi: set ts=4 sw=4 expandtab: */

0 comments on commit c49fa37

Please sign in to comment.