Allow hotplugging joysticks without udev

This commit is contained in:
Cameron Gutman 2019-06-24 21:08:26 -07:00
parent e7c2cf107a
commit a4bfe2a4ae
2 changed files with 102 additions and 53 deletions

View file

@ -34,11 +34,13 @@
#include <limits.h> /* For the definition of PATH_MAX */ #include <limits.h> /* For the definition of PATH_MAX */
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
#include <dirent.h>
#include <linux/joystick.h> #include <linux/joystick.h>
#include "SDL_assert.h" #include "SDL_assert.h"
#include "SDL_joystick.h" #include "SDL_joystick.h"
#include "SDL_endian.h" #include "SDL_endian.h"
#include "SDL_timer.h"
#include "../../events/SDL_events_c.h" #include "../../events/SDL_events_c.h"
#include "../SDL_sysjoystick.h" #include "../SDL_sysjoystick.h"
#include "../SDL_joystick_c.h" #include "../SDL_joystick_c.h"
@ -56,10 +58,8 @@
static int MaybeAddDevice(const char *path); static int MaybeAddDevice(const char *path);
#if SDL_USE_LIBUDEV #if SDL_USE_LIBUDEV
static int MaybeRemoveDevice(const char *path); static int MaybeRemoveDevice(const char *path);
static void joystick_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath);
#endif /* SDL_USE_LIBUDEV */ #endif /* SDL_USE_LIBUDEV */
/* A linked list of available joysticks */ /* A linked list of available joysticks */
typedef struct SDL_joylist_item typedef struct SDL_joylist_item
{ {
@ -79,6 +79,9 @@ static SDL_joylist_item *SDL_joylist = NULL;
static SDL_joylist_item *SDL_joylist_tail = NULL; static SDL_joylist_item *SDL_joylist_tail = NULL;
static int numjoysticks = 0; static int numjoysticks = 0;
#if !SDL_USE_LIBUDEV
static Uint32 last_joy_detect_time = 0;
#endif
#define test_bit(nr, addr) \ #define test_bit(nr, addr) \
(((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0) (((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0)
@ -177,8 +180,6 @@ static void joystick_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_clas
} }
#endif /* SDL_USE_LIBUDEV */ #endif /* SDL_USE_LIBUDEV */
/* !!! FIXME: I would love to dump this code and use libudev instead. */
static int static int
MaybeAddDevice(const char *path) MaybeAddDevice(const char *path)
{ {
@ -254,7 +255,6 @@ MaybeAddDevice(const char *path)
} }
#if SDL_USE_LIBUDEV #if SDL_USE_LIBUDEV
/* !!! FIXME: I would love to dump this code and use libudev instead. */
static int static int
MaybeRemoveDevice(const char *path) MaybeRemoveDevice(const char *path)
{ {
@ -299,45 +299,46 @@ MaybeRemoveDevice(const char *path)
} }
#endif #endif
#if ! SDL_USE_LIBUDEV static void
static int HandlePendingRemovals(void)
JoystickInitWithoutUdev(void)
{ {
int i; SDL_joylist_item *prev = NULL;
char path[PATH_MAX]; SDL_joylist_item *item = SDL_joylist;
/* !!! FIXME: only finds sticks if they're called /dev/input/event[0..31] */ while (item != NULL) {
/* !!! FIXME: we could at least readdir() through /dev/input...? */ if (item->hwdata && item->hwdata->gone) {
/* !!! FIXME: (or delete this and rely on libudev?) */ item->hwdata->item = NULL;
for (i = 0; i < 32; i++) {
SDL_snprintf(path, SDL_arraysize(path), "/dev/input/event%d", i); if (prev != NULL) {
MaybeAddDevice(path); prev->next = item->next;
} else {
SDL_assert(SDL_joylist == item);
SDL_joylist = item->next;
}
if (item == SDL_joylist_tail) {
SDL_joylist_tail = prev;
} }
return 0; /* Need to decrement the joystick count before we post the event */
} --numjoysticks;
#endif
#if SDL_USE_LIBUDEV SDL_PrivateJoystickRemoved(item->device_instance);
static int
JoystickInitWithUdev(void)
{
if (SDL_UDEV_Init() < 0) {
return SDL_SetError("Could not initialize UDEV");
}
/* Set up the udev callback */ SDL_free(item->path);
if (SDL_UDEV_AddCallback(joystick_udev_callback) < 0) { SDL_free(item->name);
SDL_UDEV_Quit(); SDL_free(item);
return SDL_SetError("Could not set up joystick <-> udev callback");
}
/* Force a scan to build the initial device list */ if (prev != NULL) {
SDL_UDEV_Scan(); item = prev->next;
} else {
return 0; item = SDL_joylist;
}
} else {
prev = item;
item = item->next;
}
}
} }
#endif
static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickGUID guid, int *device_instance) static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickGUID guid, int *device_instance)
{ {
@ -410,6 +411,42 @@ static void SteamControllerDisconnectedCallback(int device_instance)
} }
} }
static void
LINUX_JoystickDetect(void)
{
#if SDL_USE_LIBUDEV
SDL_UDEV_Poll();
#else
const Uint32 SDL_JOY_DETECT_INTERVAL_MS = 3000; /* Update every 3 seconds */
Uint32 now = SDL_GetTicks();
if (!last_joy_detect_time || SDL_TICKS_PASSED(now, last_joy_detect_time + SDL_JOY_DETECT_INTERVAL_MS)) {
DIR *folder;
struct dirent *dent;
folder = opendir("/dev/input");
if (folder) {
while ((dent = readdir(folder))) {
int len = SDL_strlen(dent->d_name);
if (len > 5 && SDL_strncmp(dent->d_name, "event", 5) == 0) {
char path[PATH_MAX];
SDL_snprintf(path, SDL_arraysize(path), "/dev/input/%s", dent->d_name);
MaybeAddDevice(path);
}
}
closedir(folder);
}
last_joy_detect_time = now;
}
#endif
HandlePendingRemovals();
SDL_UpdateSteamControllers();
}
static int static int
LINUX_JoystickInit(void) LINUX_JoystickInit(void)
{ {
@ -433,10 +470,24 @@ LINUX_JoystickInit(void)
SteamControllerDisconnectedCallback); SteamControllerDisconnectedCallback);
#if SDL_USE_LIBUDEV #if SDL_USE_LIBUDEV
return JoystickInitWithUdev(); if (SDL_UDEV_Init() < 0) {
return SDL_SetError("Could not initialize UDEV");
}
/* Set up the udev callback */
if (SDL_UDEV_AddCallback(joystick_udev_callback) < 0) {
SDL_UDEV_Quit();
return SDL_SetError("Could not set up joystick <-> udev callback");
}
/* Force a scan to build the initial device list */
SDL_UDEV_Scan();
#else #else
return JoystickInitWithoutUdev(); /* Report all devices currently present */
LINUX_JoystickDetect();
#endif #endif
return 0;
} }
static int static int
@ -445,16 +496,6 @@ LINUX_JoystickGetCount(void)
return numjoysticks; return numjoysticks;
} }
static void
LINUX_JoystickDetect(void)
{
#if SDL_USE_LIBUDEV
SDL_UDEV_Poll();
#endif
SDL_UpdateSteamControllers();
}
static SDL_joylist_item * static SDL_joylist_item *
JoystickByDevIndex(int device_index) JoystickByDevIndex(int device_index)
{ {
@ -915,6 +956,11 @@ HandleInputEvents(SDL_Joystick * joystick)
} }
} }
} }
if (errno == ENODEV) {
/* We have to wait until the JoystickDetect callback to remove this */
joystick->hwdata->gone = SDL_TRUE;
}
} }
static void static void

View file

@ -64,6 +64,9 @@ struct joystick_hwdata
SDL_bool m_bSteamController; SDL_bool m_bSteamController;
/* 4 = (ABS_HAT3X-ABS_HAT0X)/2 (see input-event-codes.h in kernel) */ /* 4 = (ABS_HAT3X-ABS_HAT0X)/2 (see input-event-codes.h in kernel) */
int hats_indices[4]; int hats_indices[4];
/* Set when gamepad is pending removal due to ENODEV read error */
SDL_bool gone;
}; };
#endif /* SDL_sysjoystick_c_h_ */ #endif /* SDL_sysjoystick_c_h_ */