diff --git a/src/core/linux/SDL_udev.c b/src/core/linux/SDL_udev.c index def0e6a8e32e8..f32a4fdf647f5 100644 --- a/src/core/linux/SDL_udev.c +++ b/src/core/linux/SDL_udev.c @@ -68,6 +68,7 @@ static bool SDL_UDEV_load_syms(void) SDL_UDEV_SYM(udev_device_get_action); SDL_UDEV_SYM(udev_device_get_devnode); + SDL_UDEV_SYM(udev_device_get_driver); SDL_UDEV_SYM(udev_device_get_syspath); SDL_UDEV_SYM(udev_device_get_subsystem); SDL_UDEV_SYM(udev_device_get_parent_with_subsystem_devtype); @@ -219,7 +220,7 @@ bool SDL_UDEV_Scan(void) return true; } -bool SDL_UDEV_GetProductInfo(const char *device_path, Uint16 *vendor, Uint16 *product, Uint16 *version, int *class) +bool SDL_UDEV_GetProductInfo(const char *device_path, struct input_id *inpid, int *class, char **driver) { struct stat statbuf; char type; @@ -253,17 +254,27 @@ bool SDL_UDEV_GetProductInfo(const char *device_path, Uint16 *vendor, Uint16 *pr val = _this->syms.udev_device_get_property_value(dev, "ID_VENDOR_ID"); if (val) { - *vendor = (Uint16)SDL_strtol(val, NULL, 16); + inpid->vendor = (Uint16)SDL_strtol(val, NULL, 16); } val = _this->syms.udev_device_get_property_value(dev, "ID_MODEL_ID"); if (val) { - *product = (Uint16)SDL_strtol(val, NULL, 16); + inpid->product = (Uint16)SDL_strtol(val, NULL, 16); } val = _this->syms.udev_device_get_property_value(dev, "ID_REVISION"); if (val) { - *version = (Uint16)SDL_strtol(val, NULL, 16); + inpid->version = (Uint16)SDL_strtol(val, NULL, 16); + } + + if (driver) { + val = _this->syms.udev_device_get_driver(dev); + if (!val) { + val = _this->syms.udev_device_get_property_value(dev, "ID_USB_DRIVER"); + } + if (val) { + *driver = SDL_strdup(val); + } } class_temp = device_class(dev); diff --git a/src/core/linux/SDL_udev.h b/src/core/linux/SDL_udev.h index 738f4bafe3f50..e99d06a5bee84 100644 --- a/src/core/linux/SDL_udev.h +++ b/src/core/linux/SDL_udev.h @@ -31,6 +31,7 @@ #endif #include +#include #include #include @@ -56,6 +57,7 @@ typedef struct SDL_UDEV_Symbols { const char *(*udev_device_get_action)(struct udev_device *); const char *(*udev_device_get_devnode)(struct udev_device *); + const char *(*udev_device_get_driver)(struct udev_device *); const char *(*udev_device_get_syspath)(struct udev_device *); const char *(*udev_device_get_subsystem)(struct udev_device *); struct udev_device *(*udev_device_get_parent_with_subsystem_devtype)(struct udev_device *udev_device, const char *subsystem, const char *devtype); @@ -102,7 +104,7 @@ extern void SDL_UDEV_UnloadLibrary(void); extern bool SDL_UDEV_LoadLibrary(void); extern void SDL_UDEV_Poll(void); extern bool SDL_UDEV_Scan(void); -extern bool SDL_UDEV_GetProductInfo(const char *device_path, Uint16 *vendor, Uint16 *product, Uint16 *version, int *class); +extern bool SDL_UDEV_GetProductInfo(const char *device_path, struct input_id *inpid, int *class, char **driver); extern bool SDL_UDEV_AddCallback(SDL_UDEV_Callback cb); extern void SDL_UDEV_DelCallback(SDL_UDEV_Callback cb); extern const SDL_UDEV_Symbols *SDL_UDEV_GetUdevSyms(void); diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c index ea73821c064d2..224a3dce37f6b 100644 --- a/src/joystick/linux/SDL_sysjoystick.c +++ b/src/joystick/linux/SDL_sysjoystick.c @@ -75,6 +75,18 @@ #ifndef BTN_DPAD_RIGHT #define BTN_DPAD_RIGHT 0x223 #endif +#ifndef BTN_GRIPL +#define BTN_GRIPL 0x224 +#endif +#ifndef BTN_GRIPR +#define BTN_GRIPR 0x225 +#endif +#ifndef BTN_GRIPL2 +#define BTN_GRIPL2 0x226 +#endif +#ifndef BTN_GRIPR2 +#define BTN_GRIPR2 0x227 +#endif #ifndef BTN_TRIGGER_HAPPY #define BTN_TRIGGER_HAPPY 0x2c0 @@ -151,6 +163,7 @@ typedef struct SDL_joylist_item SDL_JoystickID device_instance; char *path; // "/dev/input/event2" or whatever char *name; // "SideWinder 3D Pro" or whatever + char *driver; // "xpad" or whatever SDL_GUID guid; dev_t devnum; int steam_virtual_gamepad_slot; @@ -274,19 +287,20 @@ static bool GuessIsSensor(int fd) return false; } -static bool IsJoystick(const char *path, int *fd, char **name_return, Uint16 *vendor_return, Uint16 *product_return, SDL_GUID *guid) +static bool IsJoystick(const char *path, int *fd, char **name_return, Uint16 *vendor_return, Uint16 *product_return, SDL_GUID *guid, char **driver_return) { struct input_id inpid; - char *name; + char *name = NULL; + char *driver = NULL; char product_string[128]; int class = 0; SDL_zero(inpid); #ifdef SDL_USE_LIBUDEV // Opening input devices can generate synchronous device I/O, so avoid it if we can - if (SDL_UDEV_GetProductInfo(path, &inpid.vendor, &inpid.product, &inpid.version, &class) && + if (SDL_UDEV_GetProductInfo(path, &inpid, &class, &driver) && !(class & SDL_UDEV_DEVICE_JOYSTICK)) { - return false; + goto error; } #endif @@ -294,34 +308,33 @@ static bool IsJoystick(const char *path, int *fd, char **name_return, Uint16 *ve *fd = open(path, O_RDONLY | O_CLOEXEC, 0); } if (!fd || *fd < 0) { - return false; + goto error; } if (ioctl(*fd, JSIOCGNAME(sizeof(product_string)), product_string) <= 0) { // When udev enumeration or classification, we only got joysticks here, so no need to test if (enumeration_method != ENUMERATION_LIBUDEV && !class && !GuessIsJoystick(*fd)) { - return false; + goto error; } // Could have vendor and product already from udev, but should agree with evdev if (ioctl(*fd, EVIOCGID, &inpid) < 0) { - return false; + goto error; } if (ioctl(*fd, EVIOCGNAME(sizeof(product_string)), product_string) < 0) { - return false; + goto error; } } name = SDL_CreateJoystickName(inpid.vendor, inpid.product, NULL, product_string); if (!name) { - return false; + goto error; } if (!IsVirtualJoystick(inpid.vendor, inpid.product, inpid.version, name) && SDL_JoystickHandledByAnotherDriver(&SDL_LINUX_JoystickDriver, inpid.vendor, inpid.product, inpid.version, name)) { - SDL_free(name); - return false; + goto error; } FixupDeviceInfoForMapping(*fd, &inpid); @@ -331,14 +344,23 @@ static bool IsJoystick(const char *path, int *fd, char **name_return, Uint16 *ve #endif if (SDL_ShouldIgnoreJoystick(inpid.vendor, inpid.product, inpid.version, name)) { - SDL_free(name); - return false; + goto error; } *name_return = name; + *driver_return = driver; *vendor_return = inpid.vendor; *product_return = inpid.product; *guid = SDL_CreateJoystickGUID(inpid.bustype, inpid.vendor, inpid.product, inpid.version, NULL, product_string, 0, 0); return true; + +error: + if (driver) { + SDL_free(driver); + } + if (name) { + SDL_free(name); + } + return false; } static bool IsSensor(const char *path, int *fd) @@ -349,7 +371,7 @@ static bool IsSensor(const char *path, int *fd) SDL_zero(inpid); #ifdef SDL_USE_LIBUDEV // Opening input devices can generate synchronous device I/O, so avoid it if we can - if (SDL_UDEV_GetProductInfo(path, &inpid.vendor, &inpid.product, &inpid.version, &class) && + if (SDL_UDEV_GetProductInfo(path, &inpid, &class, NULL) && !(class & SDL_UDEV_DEVICE_ACCELEROMETER)) { return false; } @@ -422,6 +444,7 @@ static void FreeJoylistItem(SDL_joylist_item *item) SDL_free(item->mapping); SDL_free(item->path); SDL_free(item->name); + SDL_free(item->driver); SDL_free(item); } @@ -436,6 +459,7 @@ static void MaybeAddDevice(const char *path) struct stat sb; int fd = -1; char *name = NULL; + char *driver = NULL; Uint16 vendor, product; SDL_GUID guid; SDL_joylist_item *item; @@ -473,7 +497,7 @@ static void MaybeAddDevice(const char *path) SDL_Log("Checking %s", path); #endif - if (IsJoystick(path, &fd, &name, &vendor, &product, &guid)) { + if (IsJoystick(path, &fd, &name, &vendor, &product, &guid, &driver)) { #ifdef DEBUG_INPUT_EVENTS SDL_Log("found joystick: %s", path); #endif @@ -488,6 +512,7 @@ static void MaybeAddDevice(const char *path) item->path = SDL_strdup(path); item->name = name; item->guid = guid; + item->driver = driver; if (vendor == USB_VENDOR_VALVE && product == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD) { @@ -861,7 +886,7 @@ static void LINUX_ScanSteamVirtualGamepads(void) // Opening input devices can generate synchronous device I/O, so avoid it if we can class = 0; SDL_zero(inpid); - if (SDL_UDEV_GetProductInfo(path, &inpid.vendor, &inpid.product, &inpid.version, &class) && + if (SDL_UDEV_GetProductInfo(path, &inpid, &class, NULL) && (inpid.vendor != USB_VENDOR_VALVE || inpid.product != USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD)) { free(entries[i]); // This should NOT be SDL_free() continue; @@ -2244,6 +2269,12 @@ static bool LINUX_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping MAPPED_DPAD_LEFT = 0x4, MAPPED_DPAD_RIGHT = 0x8, MAPPED_DPAD_ALL = 0xF, + + MAPPED_LEFT_PADDLE1 = 0x1, + MAPPED_RIGHT_PADDLE1 = 0x2, + MAPPED_LEFT_PADDLE2 = 0x4, + MAPPED_RIGHT_PADDLE2 = 0x8, + MAPPED_PADDLE_ALL = 0xF, }; unsigned int mapped; bool result = false; @@ -2609,6 +2640,27 @@ static bool LINUX_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping SDL_Log("Mapped DPUP+DOWN to axis %d (ABS_HAT0Y)", out->dpup.target); SDL_Log("Mapped DPLEFT+RIGHT to axis %d (ABS_HAT0X)", out->dpleft.target); #endif + } else if (item->driver && SDL_strcmp(item->driver, "xpad") == 0) { + // xpad will sometimes map the D-Pad as BTN_TRIGGER_HAPPY1 - BTN_TRIGGER_HAPPY4 + if (joystick->hwdata->has_key[BTN_TRIGGER_HAPPY1] && + joystick->hwdata->has_key[BTN_TRIGGER_HAPPY2] && + joystick->hwdata->has_key[BTN_TRIGGER_HAPPY3] && + joystick->hwdata->has_key[BTN_TRIGGER_HAPPY4]) { + out->dpleft.kind = EMappingKind_Button; + out->dpright.kind = EMappingKind_Button; + out->dpup.kind = EMappingKind_Button; + out->dpdown.kind = EMappingKind_Button; + out->dpleft.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY1]; + out->dpright.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY2]; + out->dpup.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY3]; + out->dpdown.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY4]; +#ifdef DEBUG_GAMEPAD_MAPPING + SDL_Log("Mapped DPLEFT to button %d (BTN_TRIGGER_HAPPY1)", out->dpleft.target); + SDL_Log("Mapped DPRIGHT to button %d (BTN_TRIGGER_HAPPY2)", out->dpright.target); + SDL_Log("Mapped DPUP to button %d (BTN_TRIGGER_HAPPY3)", out->dpup.target); + SDL_Log("Mapped DPDOWN to button %d (BTN_TRIGGER_HAPPY4)", out->dpdown.target); +#endif + } } } @@ -2653,8 +2705,44 @@ static bool LINUX_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping #endif } - if (SDL_GetJoystickVendor(joystick) == USB_VENDOR_MICROSOFT) { + mapped = 0; + + if (joystick->hwdata->has_key[BTN_GRIPR]) { + out->right_paddle1.kind = EMappingKind_Button; + out->right_paddle1.target = joystick->hwdata->key_map[BTN_GRIPR]; + mapped |= MAPPED_RIGHT_PADDLE1; +#ifdef DEBUG_GAMEPAD_MAPPING + SDL_Log("Mapped RIGHT_PADDLE1 to button %d (BTN_GRIPR)", out->right_paddle1.target); +#endif + } + if (joystick->hwdata->has_key[BTN_GRIPL]) { + out->left_paddle1.kind = EMappingKind_Button; + out->left_paddle1.target = joystick->hwdata->key_map[BTN_GRIPL]; + mapped |= MAPPED_LEFT_PADDLE1; +#ifdef DEBUG_GAMEPAD_MAPPING + SDL_Log("Mapped LEFT_PADDLE1 to button %d (BTN_GRIPL)", out->left_paddle1.target); +#endif + } + if (joystick->hwdata->has_key[BTN_GRIPR2]) { + out->right_paddle2.kind = EMappingKind_Button; + out->right_paddle2.target = joystick->hwdata->key_map[BTN_GRIPR2]; + mapped |= MAPPED_RIGHT_PADDLE2; +#ifdef DEBUG_GAMEPAD_MAPPING + SDL_Log("Mapped RIGHT_PADDLE2 to button %d (BTN_GRIPR)", out->right_paddle2.target); +#endif + } + if (joystick->hwdata->has_key[BTN_GRIPL2]) { + out->left_paddle2.kind = EMappingKind_Button; + out->left_paddle2.target = joystick->hwdata->key_map[BTN_GRIPL2]; + mapped |= MAPPED_LEFT_PADDLE2; +#ifdef DEBUG_GAMEPAD_MAPPING + SDL_Log("Mapped LEFT_PADDLE2 to button %d (BTN_GRIPL2)", out->left_paddle2.target); +#endif + } + + if (mapped != MAPPED_PADDLE_ALL && SDL_GetJoystickVendor(joystick) == USB_VENDOR_MICROSOFT) { // The Xbox Elite controllers have the paddles as BTN_TRIGGER_HAPPY5 - BTN_TRIGGER_HAPPY8 + // in older drivers if (joystick->hwdata->has_key[BTN_TRIGGER_HAPPY5] && joystick->hwdata->has_key[BTN_TRIGGER_HAPPY6] && joystick->hwdata->has_key[BTN_TRIGGER_HAPPY7] && @@ -2667,6 +2755,7 @@ static bool LINUX_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping out->right_paddle2.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY6]; out->left_paddle2.kind = EMappingKind_Button; out->left_paddle2.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY8]; + mapped = MAPPED_PADDLE_ALL; #ifdef DEBUG_GAMEPAD_MAPPING SDL_Log("Mapped RIGHT_PADDLE1 to button %d (BTN_TRIGGER_HAPPY5)", out->right_paddle1.target); SDL_Log("Mapped LEFT_PADDLE1 to button %d (BTN_TRIGGER_HAPPY7)", out->left_paddle1.target); @@ -2674,15 +2763,15 @@ static bool LINUX_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping SDL_Log("Mapped LEFT_PADDLE2 to button %d (BTN_TRIGGER_HAPPY8)", out->left_paddle2.target); #endif } + } - // The Xbox Series X controllers have the Share button as KEY_RECORD - if (joystick->hwdata->has_key[KEY_RECORD]) { - out->misc1.kind = EMappingKind_Button; - out->misc1.target = joystick->hwdata->key_map[KEY_RECORD]; + // Xbox Series controllers have the Share button as KEY_RECORD + if (joystick->hwdata->has_key[KEY_RECORD]) { + out->misc1.kind = EMappingKind_Button; + out->misc1.target = joystick->hwdata->key_map[KEY_RECORD]; #ifdef DEBUG_GAMEPAD_MAPPING - SDL_Log("Mapped MISC1 to button %d (KEY_RECORD)", out->misc1.target); + SDL_Log("Mapped MISC1 to button %d (KEY_RECORD)", out->misc1.target); #endif - } } // Cache the mapping for later