From bf540985aa628634b675ea8b3cc4ffc28c98b783 Mon Sep 17 00:00:00 2001 From: Tyson Whitehead Date: Thu, 4 Apr 2024 12:17:42 -0400 Subject: [PATCH] Avoid opening non-joystick devices if possible to speedup scanning Closing a device file takes 0.01 to 0.5s, which can add up to significant startup delays. The udev classification does not require opening the actual device files, so, use it if possible, and only fall back to opening the device and probing otherwise. (cherry picked from commit 45b804c158555356e9d533e3eddd321ac8a6ee6b) --- src/joystick/linux/SDL_sysjoystick.c | 76 +++++++++++++++++++++------- 1 file changed, 57 insertions(+), 19 deletions(-) diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c index 75d25227de..8ef4c2a5d9 100644 --- a/src/joystick/linux/SDL_sysjoystick.c +++ b/src/joystick/linux/SDL_sysjoystick.c @@ -273,7 +273,7 @@ static int GuessIsSensor(int fd) return 0; } -static int IsJoystick(const char *path, int fd, char **name_return, Uint16 *vendor_return, Uint16 *product_return, SDL_JoystickGUID *guid) +static int IsJoystick(const char *path, int *fd, char **name_return, Uint16 *vendor_return, Uint16 *product_return, SDL_JoystickGUID *guid) { struct input_id inpid; char *name; @@ -282,21 +282,32 @@ static int IsJoystick(const char *path, int fd, char **name_return, Uint16 *vend SDL_zero(inpid); #ifdef SDL_USE_LIBUDEV - SDL_UDEV_GetProductInfo(path, &inpid.vendor, &inpid.product, &inpid.version, &class); + /* 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) && + !(class & SDL_UDEV_DEVICE_JOYSTICK)) { + return 0; + } #endif - if (ioctl(fd, JSIOCGNAME(sizeof(product_string)), product_string) <= 0) { - /* When udev is enabled we only get joystick devices here, so there's no need to test them */ - if (enumeration_method != ENUMERATION_LIBUDEV && - !(class & SDL_UDEV_DEVICE_JOYSTICK) && ( class || !GuessIsJoystick(fd))) { + + if (fd && *fd < 0) { + *fd = open(path, O_RDONLY | O_CLOEXEC, 0); + } + if (!fd || *fd < 0) { + return 0; + } + + 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 0; } /* Could have vendor and product already from udev, but should agree with evdev */ - if (ioctl(fd, EVIOCGID, &inpid) < 0) { + if (ioctl(*fd, EVIOCGID, &inpid) < 0) { return 0; } - if (ioctl(fd, EVIOCGNAME(sizeof(product_string)), product_string) < 0) { + if (ioctl(*fd, EVIOCGNAME(sizeof(product_string)), product_string) < 0) { return 0; } } @@ -315,7 +326,7 @@ static int IsJoystick(const char *path, int fd, char **name_return, Uint16 *vend } #endif - FixupDeviceInfoForMapping(fd, &inpid); + FixupDeviceInfoForMapping(*fd, &inpid); #ifdef DEBUG_JOYSTICK SDL_Log("Joystick: %s, bustype = %d, vendor = 0x%.4x, product = 0x%.4x, version = %d\n", name, inpid.bustype, inpid.vendor, inpid.product, inpid.version); @@ -333,11 +344,32 @@ static int IsJoystick(const char *path, int fd, char **name_return, Uint16 *vend return 1; } -static int IsSensor(const char *path, int fd) +static int IsSensor(const char *path, int *fd) { struct input_id inpid; + int class = 0; - if (ioctl(fd, EVIOCGID, &inpid) < 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) && + !(class & SDL_UDEV_DEVICE_ACCELEROMETER)) { + return 0; + } +#endif + + if (fd && *fd < 0) { + *fd = open(path, O_RDONLY | O_CLOEXEC, 0); + } + if (!fd || *fd < 0) { + return 0; + } + + if (!class && !GuessIsSensor(*fd)) { + return 0; + } + + if (ioctl(*fd, EVIOCGID, &inpid) < 0) { return 0; } @@ -347,7 +379,7 @@ static int IsSensor(const char *path, int fd) return 0; } - return GuessIsSensor(fd); + return 1; } #ifdef SDL_USE_LIBUDEV @@ -434,16 +466,11 @@ static void MaybeAddDevice(const char *path) } } - fd = open(path, O_RDONLY | O_CLOEXEC, 0); - if (fd < 0) { - goto done; - } - #ifdef DEBUG_INPUT_EVENTS SDL_Log("Checking %s\n", path); #endif - if (IsJoystick(path, fd, &name, &vendor, &product, &guid)) { + if (IsJoystick(path, &fd, &name, &vendor, &product, &guid)) { #ifdef DEBUG_INPUT_EVENTS SDL_Log("found joystick: %s\n", path); #endif @@ -484,7 +511,7 @@ static void MaybeAddDevice(const char *path) goto done; } - if (IsSensor(path, fd)) { + if (IsSensor(path, &fd)) { #ifdef DEBUG_INPUT_EVENTS SDL_Log("found sensor: %s\n", path); #endif @@ -880,11 +907,22 @@ static void LINUX_ScanSteamVirtualGamepads(void) int num_virtual_gamepads = 0; int virtual_gamepad_slot; VirtualGamepadEntry *virtual_gamepads = NULL; + int class; count = scandir("/dev/input", &entries, filter_entries, NULL); for (i = 0; i < count; ++i) { (void)SDL_snprintf(path, SDL_arraysize(path), "/dev/input/%s", entries[i]->d_name); +#ifdef SDL_USE_LIBUDEV + /* 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) && + (inpid.vendor != USB_VENDOR_VALVE || inpid.product != USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD)) { + free(entries[i]); /* This should NOT be SDL_free() */ + continue; + } +#endif fd = open(path, O_RDONLY | O_CLOEXEC, 0); if (fd >= 0) { if (ioctl(fd, EVIOCGID, &inpid) == 0 &&