joystick: Split out Linux opening code for reuse by querying code.

This prevents an assertion whem LINUX_JoystickGetGamepadMapping tried to
open the stick temporarily and messed with global state by doing so. Now
the global state is only set in LINUX_JoystickOpen, but the common code
is shared by both interfaces.

Fixes #4198.
This commit is contained in:
Ryan C. Gordon 2021-07-24 17:44:35 -04:00
parent 6c92bf540c
commit 5ae0dd4b52
No known key found for this signature in database
GPG Key ID: FA148B892AB48044
1 changed files with 62 additions and 35 deletions

View File

@ -965,6 +965,47 @@ ConfigJoystick(SDL_Joystick *joystick, int fd)
} }
/* This is used to do the heavy lifting for LINUX_JoystickOpen and
also LINUX_JoystickGetGamepadMapping, so we can query the hardware
without adding an opened SDL_Joystick object to the system.
This expects `joystick->hwdata` to be allocated and will not free it
on error. Returns -1 on error, 0 on success. */
static int
PrepareJoystickHwdata(SDL_Joystick *joystick, SDL_joylist_item *item)
{
joystick->hwdata->item = item;
joystick->hwdata->guid = item->guid;
joystick->hwdata->effect.id = -1;
joystick->hwdata->m_bSteamController = item->m_bSteamController;
SDL_memset(joystick->hwdata->abs_map, 0xFF, sizeof(joystick->hwdata->abs_map));
if (item->m_bSteamController) {
joystick->hwdata->fd = -1;
SDL_GetSteamControllerInputs(&joystick->nbuttons,
&joystick->naxes,
&joystick->nhats);
} else {
const int fd = open(item->path, O_RDWR, 0);
if (fd < 0) {
return SDL_SetError("Unable to open %s", item->path);
}
joystick->hwdata->fd = fd;
joystick->hwdata->fname = SDL_strdup(item->path);
if (joystick->hwdata->fname == NULL) {
close(fd);
return SDL_OutOfMemory();
}
/* Set the joystick to non-blocking read mode */
fcntl(fd, F_SETFL, O_NONBLOCK);
/* Get the number of buttons and axes on the joystick */
ConfigJoystick(joystick, fd);
}
}
/* Function to open a joystick for use. /* Function to open a joystick for use.
The joystick to open is specified by the device index. The joystick to open is specified by the device index.
This should fill the nbuttons and naxes fields of the joystick structure. This should fill the nbuttons and naxes fields of the joystick structure.
@ -985,39 +1026,11 @@ LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index)
if (joystick->hwdata == NULL) { if (joystick->hwdata == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
joystick->hwdata->item = item;
joystick->hwdata->guid = item->guid;
joystick->hwdata->effect.id = -1;
joystick->hwdata->m_bSteamController = item->m_bSteamController;
SDL_memset(joystick->hwdata->abs_map, 0xFF, sizeof(joystick->hwdata->abs_map));
if (item->m_bSteamController) { if (PrepareJoystickHwdata(joystick, item) == -1) {
joystick->hwdata->fd = -1; SDL_free(joystick->hwdata);
SDL_GetSteamControllerInputs(&joystick->nbuttons, joystick->hwdata = NULL;
&joystick->naxes, return -1; /* SDL_SetError will already have been called */
&joystick->nhats);
} else {
int fd = open(item->path, O_RDWR, 0);
if (fd < 0) {
SDL_free(joystick->hwdata);
joystick->hwdata = NULL;
return SDL_SetError("Unable to open %s", item->path);
}
joystick->hwdata->fd = fd;
joystick->hwdata->fname = SDL_strdup(item->path);
if (joystick->hwdata->fname == NULL) {
SDL_free(joystick->hwdata);
joystick->hwdata = NULL;
close(fd);
return SDL_OutOfMemory();
}
/* Set the joystick to non-blocking read mode */
fcntl(fd, F_SETFL, O_NONBLOCK);
/* Get the number of buttons and axes on the joystick */
ConfigJoystick(joystick, fd);
} }
SDL_assert(item->hwdata == NULL); SDL_assert(item->hwdata == NULL);
@ -1026,7 +1039,7 @@ LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index)
/* mark joystick as fresh and ready */ /* mark joystick as fresh and ready */
joystick->hwdata->fresh = SDL_TRUE; joystick->hwdata->fresh = SDL_TRUE;
return (0); return 0;
} }
static int static int
@ -1417,18 +1430,32 @@ LINUX_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
return SDL_TRUE; return SDL_TRUE;
} }
/* We temporarily open the device to check how it's configured. Make
a fake SDL_Joystick object to do so. */
joystick = (SDL_Joystick *) SDL_calloc(sizeof(*joystick), 1); joystick = (SDL_Joystick *) SDL_calloc(sizeof(*joystick), 1);
if (joystick == NULL) { if (joystick == NULL) {
SDL_OutOfMemory(); SDL_OutOfMemory();
return SDL_FALSE; return SDL_FALSE;
} }
/* We temporarily open the device to check how it's configured. */ joystick->hwdata = (struct joystick_hwdata *)
if (LINUX_JoystickOpen(joystick, device_index) < 0) { SDL_calloc(1, sizeof(*joystick->hwdata));
if (joystick->hwdata == NULL) {
SDL_free(joystick); SDL_free(joystick);
SDL_OutOfMemory();
return SDL_FALSE; return SDL_FALSE;
} }
if (PrepareJoystickHwdata(joystick, item) == -1) {
SDL_free(joystick->hwdata);
SDL_free(joystick);
return SDL_FALSE; /* SDL_SetError will already have been called */
}
/* don't assign `item->hwdata` so it's not in any global state. */
/* it is now safe to call LINUX_JoystickClose on this fake joystick. */
if (!joystick->hwdata->has_key[BTN_GAMEPAD]) { if (!joystick->hwdata->has_key[BTN_GAMEPAD]) {
/* Not a gamepad according to the specs. */ /* Not a gamepad according to the specs. */
LINUX_JoystickClose(joystick); LINUX_JoystickClose(joystick);