Skip to content

Commit

Permalink
linux: Have a table of known joysticks
Browse files Browse the repository at this point in the history
These simulation control devices are known to be joysticks, but don't
all have enough information in their evdev capabilities for us to be
able to distinguish them from an accelerometer by capabilities alone.
If we look at the bus type, vendor ID and product ID as well, then
that gives us enough information to distinguish.

Resolves: libsdl-org#7500
Co-authored-by: Felix Hädicke <[email protected]>
Signed-off-by: Simon McVittie <[email protected]>
  • Loading branch information
smcv and felixhaedicke committed Aug 6, 2024
1 parent 96e578c commit d2f3816
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 16 deletions.
24 changes: 24 additions & 0 deletions src/core/linux/SDL_evdev_capabilities.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

#ifdef HAVE_LINUX_INPUT_H

#include "../../joystick/usb_ids.h"

/* missing defines in older Linux kernel headers */
#ifndef BTN_TRIGGER_HAPPY
#define BTN_TRIGGER_HAPPY 0x2c0
Expand All @@ -36,6 +38,18 @@
#define KEY_ALS_TOGGLE 0x230
#endif

static const struct input_id known_joysticks[] = {
{ BUS_USB, USB_VENDOR_CH_PRODUCTS, USB_PRODUCT_CH_PRO_PEDALS }, /* https://github.com/libsdl-org/SDL/issues/7500 */
{ BUS_USB, USB_VENDOR_FANATEC, USB_PRODUCT_FANATEC_CLUBSPORT_USB_HANDBRAKE }, /* https://github.com/ValveSoftware/Proton/issues/5126 */
{ BUS_USB, USB_VENDOR_HEUSINKVELD, USB_PRODUCT_HEUSINKVELD_SIM_PEDALS_ULTIMATE }, /* https://github.com/ValveSoftware/Proton/issues/5126 */
{ BUS_USB, USB_VENDOR_LEO_BODNAR, USB_PRODUCT_LEO_BODNAR_G25_PEDAL_ADAPTER }, /* https://github.com/ValveSoftware/Proton/issues/5126 */
{ BUS_USB, USB_VENDOR_STMICRO, USB_PRODUCT_STMICRO_VRS_DIRECTFORCE_PRO_PEDALS }, /* https://github.com/ValveSoftware/Proton/issues/5126 */
{ BUS_USB, USB_VENDOR_THRUSTMASTER, USB_PRODUCT_THRUSTMASTER_TFRP_RUDDER }, /* https://github.com/ValveSoftware/steam-devices/pull/36 */
{ BUS_USB, USB_VENDOR_THRUSTMASTER, USB_PRODUCT_THRUSTMASTER_TWCS_THROTTLE }, /* https://github.com/ValveSoftware/steam-devices/pull/36 */
{ BUS_USB, USB_VENDOR_THRUSTMASTER, USB_PRODUCT_THRUSTMASTER_T16000M_JOYSTICK }, /* https://github.com/ValveSoftware/steam-devices/pull/36 */
};
#define N_KNOWN_JOYSTICKS (sizeof(known_joysticks) / sizeof(known_joysticks[0]))

extern int
SDL_EVDEV_GuessDeviceClass(unsigned int bus_type,
unsigned int vendor_id,
Expand Down Expand Up @@ -80,6 +94,16 @@ SDL_EVDEV_GuessDeviceClass(unsigned int bus_type,
return SDL_UDEV_DEVICE_TOUCHPAD;
}

for (i = 0; i < N_KNOWN_JOYSTICKS; i++) {
const struct input_id *known = &known_joysticks[i];

if (vendor_id == known->vendor &&
product_id == known->product &&
(known->bustype == 0 || bus_type == known->bustype)) {
return SDL_UDEV_DEVICE_JOYSTICK;
}
}

/* X, Y, Z axes but no buttons probably means an accelerometer,
* although it could equally mean 3-axis driving sim pedals -
* we can't actually tell the difference from the information
Expand Down
53 changes: 37 additions & 16 deletions test/testevdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ typedef struct
uint8_t props[(INPUT_PROP_MAX + 1) / 8];
int expected;
const char *todo;
const char *caps_insufficient;
size_t hid_report_descriptor_length;
const unsigned char *hid_report_descriptor;
} GuessTest;
Expand Down Expand Up @@ -1992,11 +1993,8 @@ static const GuessTest guess_tests[] =
.vendor_id = 0x0483, /* STMicroelectronics */
.product_id = 0xa3be, /* VRS DirectForce Pro Pedals */
.version = 0x0111,
/* TODO: Ideally we would identify this as a joystick, but there
* isn't currently enough information to do that without a table
* of known devices. */
.expected = SDL_UDEV_DEVICE_JOYSTICK,
.todo = "https://github.com/ValveSoftware/Proton/issues/5126",
.caps_insufficient = "can't distinguish from an accelerometer",
/* SYN, ABS */
.ev = { 0x09 },
/* X, Y, Z */
Expand All @@ -2010,11 +2008,8 @@ static const GuessTest guess_tests[] =
.vendor_id = 0x30b7, /* Heusinkveld Engineering */
.product_id = 0x1003, /* Heusinkveld Sim Pedals Ultimate */
.version = 0x0000,
/* TODO: Ideally we would identify this as a joystick, but there
* isn't currently enough information to do that without a table
* of known devices. */
.expected = SDL_UDEV_DEVICE_JOYSTICK,
.todo = "https://github.com/ValveSoftware/Proton/issues/5126",
.caps_insufficient = "can't distinguish from an accelerometer",
/* SYN, ABS */
.ev = { 0x09 },
/* RX, RY, RZ */
Expand Down Expand Up @@ -2045,11 +2040,8 @@ static const GuessTest guess_tests[] =
.vendor_id = 0x1dd2, /* Leo Bodnar Electronics Ltd */
.product_id = 0x100c,
.version = 0x0110,
/* TODO: Ideally we would identify this as a joystick, but there
* isn't currently enough information to do that without a table
* of known devices. */
.expected = SDL_UDEV_DEVICE_JOYSTICK,
.todo = "https://github.com/ValveSoftware/Proton/issues/5126",
.caps_insufficient = "can't distinguish from an accelerometer",
/* SYN, ABS */
.ev = { 0x09 },
/* RX, RY, RZ */
Expand All @@ -2061,11 +2053,8 @@ static const GuessTest guess_tests[] =
.vendor_id = 0x0eb7,
.product_id = 0x1a93,
.version = 0x0111,
/* TODO: Ideally we would identify this as a joystick, but there
* isn't currently enough information to do that without a table
* of known devices. */
.expected = SDL_UDEV_DEVICE_JOYSTICK,
.todo = "https://github.com/ValveSoftware/Proton/issues/5126",
.caps_insufficient = "X axis is not enough to guess device class",
/* SYN, ABS */
.ev = { 0x09 },
/* X only */
Expand Down Expand Up @@ -2206,6 +2195,38 @@ run_test(void)
success = 0;
}
}

actual = SDL_EVDEV_GuessDeviceClass(0, 0, 0, 0,
caps.props, caps.ev, caps.abs,
caps.keys, caps.rel);

if (actual == t->expected) {
printf("\tOK\n");
} else {
printf("\tExpected 0x%08x\n", t->expected);

for (j = 0; device_classes[j].code != 0; j++) {
if (t->expected & device_classes[j].code) {
printf("\t\t%s\n", device_classes[j].name);
}
}

printf("\tGot 0x%08x using only evdev caps\n", actual);

for (j = 0; device_classes[j].code != 0; j++) {
if (actual & device_classes[j].code) {
printf("\t\t%s\n", device_classes[j].name);
}
}

if (t->caps_insufficient) {
printf("\tKnown limitation, evdev caps are not enough information: %s\n",
t->caps_insufficient);
} else {
printf("\tFailed\n");
success = 0;
}
}
}

return success;
Expand Down

0 comments on commit d2f3816

Please sign in to comment.