Try SDL_UDEV_deviceclass to detect joysticks even if in a container

The udev container issue is mostly to do with device notifications
and netlink. The device classification stuff just pokes file in /sys
and /run/udev. Doesn't hurt to try it first for classifying joysticks
and then fall to the guess heuristics if it fails.
This commit is contained in:
Tyson Whitehead 2023-11-29 18:19:03 -05:00 committed by Sam Lantinga
parent 1bf78ed544
commit 6daf2e943f
3 changed files with 42 additions and 22 deletions

View file

@ -42,6 +42,9 @@ static SDL_UDEV_PrivateData *_this = NULL;
static SDL_bool SDL_UDEV_load_sym(const char *fn, void **addr); static SDL_bool SDL_UDEV_load_sym(const char *fn, void **addr);
static int SDL_UDEV_load_syms(void); static int SDL_UDEV_load_syms(void);
static SDL_bool SDL_UDEV_hotplug_update_available(void); static SDL_bool SDL_UDEV_hotplug_update_available(void);
static void get_caps(struct udev_device *dev, struct udev_device *pdev, const char *attr, unsigned long *bitmask, size_t bitmask_len);
static int guess_device_class(struct udev_device *dev);
static int device_class(struct udev_device *dev);
static void device_event(SDL_UDEV_deviceevent type, struct udev_device *dev); static void device_event(SDL_UDEV_deviceevent type, struct udev_device *dev);
static SDL_bool SDL_UDEV_load_sym(const char *fn, void **addr) static SDL_bool SDL_UDEV_load_sym(const char *fn, void **addr)
@ -218,7 +221,7 @@ int SDL_UDEV_Scan(void)
return 0; return 0;
} }
SDL_bool SDL_UDEV_GetProductInfo(const char *device_path, Uint16 *vendor, Uint16 *product, Uint16 *version) SDL_bool SDL_UDEV_GetProductInfo(const char *device_path, Uint16 *vendor, Uint16 *product, Uint16 *version, int *class)
{ {
struct udev_enumerate *enumerate = NULL; struct udev_enumerate *enumerate = NULL;
struct udev_list_entry *devs = NULL; struct udev_list_entry *devs = NULL;
@ -246,6 +249,7 @@ SDL_bool SDL_UDEV_GetProductInfo(const char *device_path, Uint16 *vendor, Uint16
existing_path = _this->syms.udev_device_get_devnode(dev); existing_path = _this->syms.udev_device_get_devnode(dev);
if (existing_path && SDL_strcmp(device_path, existing_path) == 0) { if (existing_path && SDL_strcmp(device_path, existing_path) == 0) {
int class_temp;
found = SDL_TRUE; found = SDL_TRUE;
val = _this->syms.udev_device_get_property_value(dev, "ID_VENDOR_ID"); val = _this->syms.udev_device_get_property_value(dev, "ID_VENDOR_ID");
@ -262,6 +266,11 @@ SDL_bool SDL_UDEV_GetProductInfo(const char *device_path, Uint16 *vendor, Uint16
if (val) { if (val) {
*version = (Uint16)SDL_strtol(val, NULL, 16); *version = (Uint16)SDL_strtol(val, NULL, 16);
} }
class_temp = device_class(dev);
if (class_temp) {
*class = class_temp;
}
} }
_this->syms.udev_device_unref(dev); _this->syms.udev_device_unref(dev);
} }
@ -393,29 +402,23 @@ static int guess_device_class(struct udev_device *dev)
&bitmask_rel[0]); &bitmask_rel[0]);
} }
static void device_event(SDL_UDEV_deviceevent type, struct udev_device *dev) static int device_class(struct udev_device *dev)
{ {
const char *subsystem; const char *subsystem;
const char *val = NULL; const char *val = NULL;
int devclass = 0; int devclass = 0;
const char *path;
SDL_UDEV_CallbackList *item;
path = _this->syms.udev_device_get_devnode(dev);
if (!path) {
return;
}
subsystem = _this->syms.udev_device_get_subsystem(dev); subsystem = _this->syms.udev_device_get_subsystem(dev);
if (!subsystem) {
return 0;
}
if (SDL_strcmp(subsystem, "sound") == 0) { if (SDL_strcmp(subsystem, "sound") == 0) {
devclass = SDL_UDEV_DEVICE_SOUND; devclass = SDL_UDEV_DEVICE_SOUND;
} else if (SDL_strcmp(subsystem, "video4linux") == 0) { } else if (SDL_strcmp(subsystem, "video4linux") == 0) {
devclass = SDL_UDEV_DEVICE_VIDEO_CAPTURE;
val = _this->syms.udev_device_get_property_value(dev, "ID_V4L_CAPABILITIES"); val = _this->syms.udev_device_get_property_value(dev, "ID_V4L_CAPABILITIES");
if (!val || !SDL_strcasestr(val, "capture")) { if (val && SDL_strcasestr(val, "capture")) {
return; devclass = SDL_UDEV_DEVICE_VIDEO_CAPTURE;
} }
} else if (SDL_strcmp(subsystem, "input") == 0) { } else if (SDL_strcmp(subsystem, "input") == 0) {
/* udev rules reference: http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-input_id.c */ /* udev rules reference: http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-input_id.c */
@ -467,15 +470,30 @@ static void device_event(SDL_UDEV_deviceevent type, struct udev_device *dev)
devclass = SDL_UDEV_DEVICE_MOUSE; devclass = SDL_UDEV_DEVICE_MOUSE;
} else if (SDL_strcmp(val, "kbd") == 0) { } else if (SDL_strcmp(val, "kbd") == 0) {
devclass = SDL_UDEV_DEVICE_HAS_KEYS | SDL_UDEV_DEVICE_KEYBOARD; devclass = SDL_UDEV_DEVICE_HAS_KEYS | SDL_UDEV_DEVICE_KEYBOARD;
} else {
return;
} }
} else { } else {
/* We could be linked with libudev on a system that doesn't have udev running */ /* We could be linked with libudev on a system that doesn't have udev running */
devclass = guess_device_class(dev); devclass = guess_device_class(dev);
} }
} }
} else { }
return devclass;
}
static void device_event(SDL_UDEV_deviceevent type, struct udev_device *dev)
{
int devclass = 0;
const char *path;
SDL_UDEV_CallbackList *item;
path = _this->syms.udev_device_get_devnode(dev);
if (!path) {
return;
}
devclass = device_class(dev);
if (!devclass) {
return; return;
} }

View file

@ -102,7 +102,7 @@ extern void SDL_UDEV_UnloadLibrary(void);
extern int SDL_UDEV_LoadLibrary(void); extern int SDL_UDEV_LoadLibrary(void);
extern void SDL_UDEV_Poll(void); extern void SDL_UDEV_Poll(void);
extern int SDL_UDEV_Scan(void); extern int SDL_UDEV_Scan(void);
extern SDL_bool SDL_UDEV_GetProductInfo(const char *device_path, Uint16 *vendor, Uint16 *product, Uint16 *version); extern SDL_bool SDL_UDEV_GetProductInfo(const char *device_path, Uint16 *vendor, Uint16 *product, Uint16 *version, int *class);
extern int SDL_UDEV_AddCallback(SDL_UDEV_Callback cb); extern int SDL_UDEV_AddCallback(SDL_UDEV_Callback cb);
extern void SDL_UDEV_DelCallback(SDL_UDEV_Callback cb); extern void SDL_UDEV_DelCallback(SDL_UDEV_Callback cb);
extern const SDL_UDEV_Symbols *SDL_UDEV_GetUdevSyms(void); extern const SDL_UDEV_Symbols *SDL_UDEV_GetUdevSyms(void);

View file

@ -282,18 +282,20 @@ static int IsJoystick(const char *path, int fd, char **name_return, Uint16 *vend
struct input_id inpid; struct input_id inpid;
char *name; char *name;
char product_string[128]; char product_string[128];
int class = 0;
if (ioctl(fd, JSIOCGNAME(sizeof(product_string)), product_string) >= 0) {
SDL_zero(inpid); SDL_zero(inpid);
#ifdef SDL_USE_LIBUDEV #ifdef SDL_USE_LIBUDEV
SDL_UDEV_GetProductInfo(path, &inpid.vendor, &inpid.product, &inpid.version); SDL_UDEV_GetProductInfo(path, &inpid.vendor, &inpid.product, &inpid.version, &class);
#endif #endif
} else { 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 */ /* When udev is enabled we only get joystick devices here, so there's no need to test them */
if (enumeration_method != ENUMERATION_LIBUDEV && !GuessIsJoystick(fd)) { if (enumeration_method != ENUMERATION_LIBUDEV &&
!(class & SDL_UDEV_DEVICE_JOYSTICK) && ( class || !GuessIsJoystick(fd))) {
return 0; 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; return 0;
} }