Skip to content

Commit

Permalink
Try SDL_UDEV_deviceclass to detect joysticks even if in a container
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
twhitehead authored and slouken committed Dec 25, 2023
1 parent 857e5b0 commit 3b1e0e1
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 17 deletions.
43 changes: 32 additions & 11 deletions src/core/linux/SDL_udev.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ static _THIS = NULL;
static SDL_bool SDL_UDEV_load_sym(const char *fn, void **addr);
static int SDL_UDEV_load_syms(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 SDL_bool SDL_UDEV_load_sym(const char *fn, void **addr)
Expand Down Expand Up @@ -222,7 +225,7 @@ void SDL_UDEV_Scan(void)
_this->syms.udev_enumerate_unref(enumerate);
}

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_list_entry *devs = NULL;
Expand Down Expand Up @@ -250,6 +253,7 @@ SDL_bool SDL_UDEV_GetProductInfo(const char *device_path, Uint16 *vendor, Uint16

existing_path = _this->syms.udev_device_get_devnode(dev);
if (existing_path && SDL_strcmp(device_path, existing_path) == 0) {
int class_temp;
found = SDL_TRUE;

val = _this->syms.udev_device_get_property_value(dev, "ID_VENDOR_ID");
Expand All @@ -266,6 +270,11 @@ SDL_bool SDL_UDEV_GetProductInfo(const char *device_path, Uint16 *vendor, Uint16
if (val) {
*version = (Uint16)SDL_strtol(val, NULL, 16);
}

class_temp = device_class(dev);
if (class_temp) {
*class = class_temp;
}
}
_this->syms.udev_device_unref(dev);
}
Expand Down Expand Up @@ -394,20 +403,17 @@ static int guess_device_class(struct udev_device *dev)
&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 *val = NULL;
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);
if (!subsystem) {
return 0;
}

subsystem = _this->syms.udev_device_get_subsystem(dev);
if (SDL_strcmp(subsystem, "sound") == 0) {
devclass = SDL_UDEV_DEVICE_SOUND;
} else if (SDL_strcmp(subsystem, "input") == 0) {
Expand Down Expand Up @@ -455,18 +461,33 @@ static void device_event(SDL_UDEV_deviceevent type, struct udev_device *dev)
devclass = SDL_UDEV_DEVICE_MOUSE;
} else if (SDL_strcmp(val, "kbd") == 0) {
devclass = SDL_UDEV_DEVICE_KEYBOARD;
} else {
return;
}
} else {
/* We could be linked with libudev on a system that doesn't have udev running */
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;
}

/* Process callbacks */
for (item = _this->first; item; item = item->next) {
item->callback(type, devclass, path);
Expand Down
2 changes: 1 addition & 1 deletion src/core/linux/SDL_udev.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ extern void SDL_UDEV_UnloadLibrary(void);
extern int SDL_UDEV_LoadLibrary(void);
extern void SDL_UDEV_Poll(void);
extern void 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 void SDL_UDEV_DelCallback(SDL_UDEV_Callback cb);
extern const SDL_UDEV_Symbols *SDL_UDEV_GetUdevSyms(void);
Expand Down
12 changes: 7 additions & 5 deletions src/joystick/linux/SDL_sysjoystick.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,18 +278,20 @@ static int IsJoystick(const char *path, int fd, char **name_return, Uint16 *vend
struct input_id inpid;
char *name;
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
SDL_UDEV_GetProductInfo(path, &inpid.vendor, &inpid.product, &inpid.version);
SDL_UDEV_GetProductInfo(path, &inpid.vendor, &inpid.product, &inpid.version, &class);
#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 */
if (enumeration_method != ENUMERATION_LIBUDEV && !GuessIsJoystick(fd)) {
if (enumeration_method != ENUMERATION_LIBUDEV &&
!(class & SDL_UDEV_DEVICE_JOYSTICK) && ( class || !GuessIsJoystick(fd))) {
return 0;
}

/* Could have vendor and product already from udev, but should agree with evdev */
if (ioctl(fd, EVIOCGID, &inpid) < 0) {
return 0;
}
Expand Down

0 comments on commit 3b1e0e1

Please sign in to comment.