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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c
index f0c2d5c..3e267e3 100644
--- a/src/joystick/linux/SDL_sysjoystick.c
+++ b/src/joystick/linux/SDL_sysjoystick.c
@@ -965,26 +965,14 @@ ConfigJoystick(SDL_Joystick *joystick, int fd)
}
-/* Function to open a joystick for use.
- The joystick to open is specified by the device index.
- This should fill the nbuttons and naxes fields of the joystick structure.
- It returns 0, or -1 if there is an error.
- */
+/* 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
-LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index)
+PrepareJoystickHwdata(SDL_Joystick *joystick, SDL_joylist_item *item)
{
- SDL_joylist_item *item = JoystickByDevIndex(device_index);
-
- if (item == NULL) {
- return SDL_SetError("No such device");
- }
-
- joystick->instance_id = item->device_instance;
- joystick->hwdata = (struct joystick_hwdata *)
- SDL_calloc(1, sizeof(*joystick->hwdata));
- if (joystick->hwdata == NULL) {
- return SDL_OutOfMemory();
- }
joystick->hwdata->item = item;
joystick->hwdata->guid = item->guid;
joystick->hwdata->effect.id = -1;
@@ -997,18 +985,14 @@ LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index)
&joystick->naxes,
&joystick->nhats);
} else {
- int fd = open(item->path, O_RDWR, 0);
+ const 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();
}
@@ -1019,6 +1003,35 @@ LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index)
/* Get the number of buttons and axes on the joystick */
ConfigJoystick(joystick, fd);
}
+}
+
+
+/* Function to open a joystick for use.
+ The joystick to open is specified by the device index.
+ This should fill the nbuttons and naxes fields of the joystick structure.
+ It returns 0, or -1 if there is an error.
+ */
+static int
+LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index)
+{
+ SDL_joylist_item *item = JoystickByDevIndex(device_index);
+
+ if (item == NULL) {
+ return SDL_SetError("No such device");
+ }
+
+ joystick->instance_id = item->device_instance;
+ joystick->hwdata = (struct joystick_hwdata *)
+ SDL_calloc(1, sizeof(*joystick->hwdata));
+ if (joystick->hwdata == NULL) {
+ return SDL_OutOfMemory();
+ }
+
+ if (PrepareJoystickHwdata(joystick, item) == -1) {
+ SDL_free(joystick->hwdata);
+ joystick->hwdata = NULL;
+ return -1; /* SDL_SetError will already have been called */
+ }
SDL_assert(item->hwdata == NULL);
item->hwdata = joystick->hwdata;
@@ -1026,7 +1039,7 @@ LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index)
/* mark joystick as fresh and ready */
joystick->hwdata->fresh = SDL_TRUE;
- return (0);
+ return 0;
}
static int
@@ -1417,18 +1430,32 @@ LINUX_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
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);
if (joystick == NULL) {
SDL_OutOfMemory();
return SDL_FALSE;
}
- /* We temporarily open the device to check how it's configured. */
- if (LINUX_JoystickOpen(joystick, device_index) < 0) {
+ joystick->hwdata = (struct joystick_hwdata *)
+ SDL_calloc(1, sizeof(*joystick->hwdata));
+ if (joystick->hwdata == NULL) {
SDL_free(joystick);
+ SDL_OutOfMemory();
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]) {
/* Not a gamepad according to the specs. */
LINUX_JoystickClose(joystick);