From 020f608e143b9fe34e8a28564bfd6116cd24b4cd Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 13 Jun 2023 18:39:29 +0100 Subject: [PATCH] linux: Have a table of known joysticks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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: https://github.com/libsdl-org/SDL/issues/7500 Co-authored-by: Felix Hädicke Signed-off-by: Simon McVittie --- src/core/linux/SDL_evdev_capabilities.c | 24 +++++++++++ test/testevdev.c | 53 +++++++++++++++++-------- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/src/core/linux/SDL_evdev_capabilities.c b/src/core/linux/SDL_evdev_capabilities.c index 2d4e937d9c863c..f13c8a954561ef 100644 --- a/src/core/linux/SDL_evdev_capabilities.c +++ b/src/core/linux/SDL_evdev_capabilities.c @@ -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 @@ -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, @@ -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 diff --git a/test/testevdev.c b/test/testevdev.c index 50e2f0d8a141da..60ca0c6c90390e 100644 --- a/test/testevdev.c +++ b/test/testevdev.c @@ -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; @@ -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 */ @@ -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 */ @@ -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 */ @@ -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 */ @@ -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;