ref: 206603577aa81b4184a36ae38d5cc1307d6971ac
parent: 79d38c7d1ef051aad898eb45e965f3660bffc763
author: Simon Howard <fraggle@soulsphere.org>
date: Sun May 8 13:06:09 EDT 2016
joystick: Use SDL GUID to identify joysticks. The current configuration system uses joystick_index to identify the joystick which is configured. This has the downside of assuming a static configuration; joystick indexes can change if devices are plugged or unplugged. SDL2 introduces the idea of Joystick GUIDs which can uniquely identify a class of device, so use this as the primary configuration variable instead; that way, if the number or ordering of joystick devices changes, we will still use the same device. As GUID can only identify a "class" of device (eg. "Xbox controller"), we still keep joystick_index around to try to differentiate between devices when there are multiple identical devices connected.
--- a/src/i_joystick.c
+++ b/src/i_joystick.c
@@ -44,8 +44,8 @@
static int usejoystick = 0;
-// Joystick to use, as an SDL joystick index:
-
+// SDL GUID and index of the joystick to use.
+static char *joystick_guid = "";
static int joystick_index = -1;
// Which joystick axis to use for horizontal movement, and whether to
@@ -105,9 +105,45 @@
return axis < num_axes;
}
+static int DeviceIndex(void)
+{
+ SDL_JoystickGUID guid, dev_guid;
+ int i;
+
+ guid = SDL_JoystickGetGUIDFromString(joystick_guid);
+
+ // GUID identifies a class of device rather than a specific device.
+ // Check if joystick_index has the expected GUID, as this can act
+ // as a tie-breaker in case there are multiple identical devices.
+ if (joystick_index >= 0 && joystick_index < SDL_NumJoysticks())
+ {
+ dev_guid = SDL_JoystickGetDeviceGUID(joystick_index);
+ if (!memcmp(&guid, &dev_guid, sizeof(SDL_JoystickGUID)))
+ {
+ return joystick_index;
+ }
+ }
+
+ // Check all devices to look for one with the expected GUID.
+ for (i = 0; i < SDL_NumJoysticks(); ++i)
+ {
+ dev_guid = SDL_JoystickGetDeviceGUID(i);
+ if (!memcmp(&guid, &dev_guid, sizeof(SDL_JoystickGUID)))
+ {
+ printf("I_InitJoystick: Joystick moved to index %d.\n", i);
+ return i;
+ }
+ }
+
+ // No joystick found with the expected GUID.
+ return -1;
+}
+
void I_InitJoystick(void)
{
- if (!usejoystick || joystick_index < 0)
+ int index;
+
+ if (!usejoystick || !strcmp(joystick_guid, ""))
{
return;
}
@@ -117,9 +153,13 @@
return;
}
- if (joystick_index >= SDL_NumJoysticks())
+ index = DeviceIndex();
+
+ if (index < 0)
{
- printf("I_InitJoystick: Invalid joystick ID: %i\n", joystick_index);
+ printf("I_InitJoystick: Couldn't find joystick with GUID \"%s\": "
+ "device not found or not connected?\n",
+ joystick_guid);
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
return;
}
@@ -126,12 +166,11 @@
// Open the joystick
- joystick = SDL_JoystickOpen(joystick_index);
+ joystick = SDL_JoystickOpen(index);
if (joystick == NULL)
{
- printf("I_InitJoystick: Failed to open joystick #%i\n",
- joystick_index);
+ printf("I_InitJoystick: Failed to open joystick #%i\n", index);
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
return;
}
@@ -140,9 +179,8 @@
|| !IsValidAxis(joystick_y_axis)
|| !IsValidAxis(joystick_strafe_axis))
{
- printf("I_InitJoystick: Invalid joystick axis for joystick #%i "
- "(run joystick setup again)\n",
- joystick_index);
+ printf("I_InitJoystick: Invalid joystick axis for configured joystick "
+ "(run joystick setup again)\n");
SDL_JoystickClose(joystick);
joystick = NULL;
@@ -329,6 +367,7 @@
int i;
M_BindIntVariable("use_joystick", &usejoystick);
+ M_BindStringVariable("joystick_guid", &joystick_guid);
M_BindIntVariable("joystick_index", &joystick_index);
M_BindIntVariable("joystick_x_axis", &joystick_x_axis);
M_BindIntVariable("joystick_y_axis", &joystick_y_axis);
--- a/src/m_config.c
+++ b/src/m_config.c
@@ -911,8 +911,16 @@
#endif
//!
- // Joystick number to use; '0' is the first joystick. A negative
- // value ('-1') indicates that no joystick is configured.
+ // SDL GUID string indicating the joystick to use. An empty string
+ // indicates that no joystick is configured.
+ //
+
+ CONFIG_VARIABLE_STRING(joystick_guid),
+
+ //!
+ // Index of SDL joystick to use; this is only used in the case where
+ // multiple identical joystick devices are connected which have the
+ // same GUID, to distinguish between devices.
//
CONFIG_VARIABLE_INT(joystick_index),
--- a/src/setup/joystick.c
+++ b/src/setup/joystick.c
@@ -52,8 +52,9 @@
static int usejoystick = 0;
-// Joystick to use, as an SDL joystick index:
+// GUID and index of joystick to use.
+char *joystick_guid = "";
int joystick_index = -1;
// Calibration button. This is the button the user pressed at the
@@ -94,6 +95,7 @@
static txt_window_t *calibration_window;
static SDL_Joystick **all_joysticks = NULL;
+static int all_joysticks_len = 0;
// Known controllers.
// There are lots of game controllers on the market. Try to configure
@@ -529,25 +531,71 @@
}
}
-// Set the label showing the name of the currently selected joystick
-
-static void SetJoystickButtonLabel(void)
+// We identify joysticks using GUID where possible, but joystick_index
+// is used to distinguish between different devices. As the index can
+// change, UpdateJoystickIndex() checks to see if it is still valid and
+// updates it as appropriate.
+static void UpdateJoystickIndex(void)
{
- char *name;
+ SDL_JoystickGUID guid, dev_guid;
+ int i;
- InitJoystick();
+ guid = SDL_JoystickGetGUIDFromString(joystick_guid);
- name = "None set";
+ // Is joystick_index already correct?
+ if (joystick_index >= 0 && joystick_index < SDL_NumJoysticks())
+ {
+ dev_guid = SDL_JoystickGetDeviceGUID(joystick_index);
+ if (!memcmp(&guid, &dev_guid, sizeof(SDL_JoystickGUID)))
+ {
+ return;
+ }
+ }
- if (joystick_initted
- && joystick_index >= 0 && joystick_index < SDL_NumJoysticks())
+ // If index is not correct, look for the first device with the
+ // expected GUID. It may have moved to a different index.
+ for (i = 0; i < SDL_NumJoysticks(); ++i)
{
- name = (char *) SDL_JoystickNameForIndex(joystick_index);
+ dev_guid = SDL_JoystickGetDeviceGUID(i);
+ if (!memcmp(&guid, &dev_guid, sizeof(SDL_JoystickGUID)))
+ {
+ joystick_index = i;
+ return;
+ }
}
- TXT_SetButtonLabel(joystick_button, name);
+ // Not found; it's possible the device is disconnected. Do not
+ // reset joystick_guid or joystick_index in case they are
+ // reconnected later.
+}
- UnInitJoystick();
+// Set the label showing the name of the currently selected joystick
+static void SetJoystickButtonLabel(void)
+{
+ SDL_JoystickGUID guid, dev_guid;
+ const char *name;
+
+ if (!usejoystick || !strcmp(joystick_guid, ""))
+ {
+ name = "None set";
+ }
+ else
+ {
+ name = "Not found (device disconnected?)";
+
+ // Use the device name if the GUID and index match.
+ if (joystick_index >= 0 && joystick_index < SDL_NumJoysticks())
+ {
+ guid = SDL_JoystickGetGUIDFromString(joystick_guid);
+ dev_guid = SDL_JoystickGetDeviceGUID(joystick_index);
+ if (!memcmp(&guid, &dev_guid, sizeof(SDL_JoystickGUID)))
+ {
+ name = SDL_JoystickNameForIndex(joystick_index);
+ }
+ }
+ }
+
+ TXT_SetButtonLabel(joystick_button, (char *) name);
}
// Try to open all joysticks visible to SDL.
@@ -555,7 +603,6 @@
static int OpenAllJoysticks(void)
{
int i;
- int num_joysticks;
int result;
InitJoystick();
@@ -562,12 +609,12 @@
// SDL_JoystickOpen() all joysticks.
- num_joysticks = SDL_NumJoysticks();
- all_joysticks = calloc(num_joysticks, sizeof(SDL_Joystick *));
+ all_joysticks_len = SDL_NumJoysticks();
+ all_joysticks = calloc(all_joysticks_len, sizeof(SDL_Joystick *));
result = 0;
- for (i = 0; i < num_joysticks; ++i)
+ for (i = 0; i < all_joysticks_len; ++i)
{
all_joysticks[i] = SDL_JoystickOpen(i);
@@ -599,11 +646,8 @@
static void CloseAllJoysticks(void)
{
int i;
- int num_joysticks;
- num_joysticks = SDL_NumJoysticks();
-
- for (i = 0; i < num_joysticks; ++i)
+ for (i = 0; i < all_joysticks_len; ++i)
{
if (all_joysticks[i] != NULL)
{
@@ -624,20 +668,34 @@
TXT_ConfigureJoystickAxis(x_axis_widget, calibrate_button, NULL);
}
-// TODO: Remove once we no longer use joystick_index in .cfg files.
-static int JoystickIDToIndex(int joy_id)
+// Given the SDL_JoystickID instance ID from a button event, set the
+// joystick_guid and joystick_index config variables.
+static boolean SetJoystickGUID(SDL_JoystickID joy_id)
{
- SDL_Joystick *joystick = SDL_JoystickFromInstanceID(joy_id);
+ SDL_Joystick *joystick;
+ SDL_JoystickGUID guid;
int i;
- for (i = 0; i < SDL_NumJoysticks(); ++i)
+ joystick = SDL_JoystickFromInstanceID(joy_id);
+ if (joystick == NULL)
{
- if (joystick == all_joysticks[i])
+ return false;
+ }
+
+ guid = SDL_JoystickGetGUID(joystick);
+ joystick_guid = malloc(33);
+ SDL_JoystickGetGUIDString(guid, joystick_guid, 33);
+
+ for (i = 0; i < all_joysticks_len; ++i)
+ {
+ if (all_joysticks[i] == joystick)
{
- return i;
+ joystick_index = i;
+ return true;
}
}
- return -1;
+
+ return false;
}
static int CalibrationEventCallback(SDL_Event *event, void *user_data)
@@ -647,18 +705,17 @@
return 0;
}
+ if (!SetJoystickGUID(event->jbutton.which))
+ {
+ return 0;
+ }
+
// At this point, we have a button press.
// In the first "center" stage, we're just trying to work out which
// joystick is being configured and which button the user is pressing.
usejoystick = 1;
- joystick_index = JoystickIDToIndex(event->jbutton.which);
calibrate_button = event->jbutton.button;
- if (joystick_index < 0)
- {
- return 0;
- }
-
// If the joystick is a known one, auto-load default
// config for it. Otherwise, proceed with calibration.
if (IsKnownJoystick(joystick_index))
@@ -693,9 +750,9 @@
static void CalibrateWindowClosed(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
{
- CloseAllJoysticks();
TXT_SDL_SetEventCallback(NULL, NULL);
SetJoystickButtonLabel();
+ CloseAllJoysticks();
}
static void CalibrateJoystick(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
@@ -826,7 +883,10 @@
TXT_SignalConnect(joystick_button, "pressed", CalibrateJoystick, NULL);
TXT_SetWindowAction(window, TXT_HORIZ_CENTER, TestConfigAction());
+ InitJoystick();
+ UpdateJoystickIndex();
SetJoystickButtonLabel();
+ UnInitJoystick();
}
void BindJoystickVariables(void)
@@ -834,6 +894,7 @@
int i;
M_BindIntVariable("use_joystick", &usejoystick);
+ M_BindStringVariable("joystick_guid", &joystick_guid);
M_BindIntVariable("joystick_index", &joystick_index);
M_BindIntVariable("joystick_x_axis", &joystick_x_axis);
M_BindIntVariable("joystick_y_axis", &joystick_y_axis);