From a4bfe2a4ae371a00fa369e14369abec78886bacb Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Mon, 24 Jun 2019 21:08:26 -0700 Subject: [PATCH] Allow hotplugging joysticks without udev --- src/joystick/linux/SDL_sysjoystick.c | 146 ++++++++++++++++--------- src/joystick/linux/SDL_sysjoystick_c.h | 3 + 2 files changed, 99 insertions(+), 50 deletions(-) diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c index 89a5eb89982fa..ad830919677c1 100644 --- a/src/joystick/linux/SDL_sysjoystick.c +++ b/src/joystick/linux/SDL_sysjoystick.c @@ -34,11 +34,13 @@ #include /* For the definition of PATH_MAX */ #include #include +#include #include #include "SDL_assert.h" #include "SDL_joystick.h" #include "SDL_endian.h" +#include "SDL_timer.h" #include "../../events/SDL_events_c.h" #include "../SDL_sysjoystick.h" #include "../SDL_joystick_c.h" @@ -56,10 +58,8 @@ static int MaybeAddDevice(const char *path); #if SDL_USE_LIBUDEV 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 */ - /* A linked list of available joysticks */ 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 int numjoysticks = 0; +#if !SDL_USE_LIBUDEV +static Uint32 last_joy_detect_time = 0; +#endif #define test_bit(nr, addr) \ (((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 */ - -/* !!! FIXME: I would love to dump this code and use libudev instead. */ static int MaybeAddDevice(const char *path) { @@ -254,7 +255,6 @@ MaybeAddDevice(const char *path) } #if SDL_USE_LIBUDEV -/* !!! FIXME: I would love to dump this code and use libudev instead. */ static int MaybeRemoveDevice(const char *path) { @@ -299,45 +299,46 @@ MaybeRemoveDevice(const char *path) } #endif -#if ! SDL_USE_LIBUDEV -static int -JoystickInitWithoutUdev(void) +static void +HandlePendingRemovals(void) { - int i; - char path[PATH_MAX]; + SDL_joylist_item *prev = NULL; + SDL_joylist_item *item = SDL_joylist; - /* !!! FIXME: only finds sticks if they're called /dev/input/event[0..31] */ - /* !!! FIXME: we could at least readdir() through /dev/input...? */ - /* !!! FIXME: (or delete this and rely on libudev?) */ - for (i = 0; i < 32; i++) { - SDL_snprintf(path, SDL_arraysize(path), "/dev/input/event%d", i); - MaybeAddDevice(path); - } + while (item != NULL) { + if (item->hwdata && item->hwdata->gone) { + item->hwdata->item = NULL; - return 0; -} -#endif + if (prev != NULL) { + prev->next = item->next; + } else { + SDL_assert(SDL_joylist == item); + SDL_joylist = item->next; + } + if (item == SDL_joylist_tail) { + SDL_joylist_tail = prev; + } -#if SDL_USE_LIBUDEV -static int -JoystickInitWithUdev(void) -{ - if (SDL_UDEV_Init() < 0) { - return SDL_SetError("Could not initialize UDEV"); - } + /* Need to decrement the joystick count before we post the event */ + --numjoysticks; - /* 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(); + SDL_PrivateJoystickRemoved(item->device_instance); - return 0; + SDL_free(item->path); + SDL_free(item->name); + SDL_free(item); + + if (prev != NULL) { + item = prev->next; + } else { + item = SDL_joylist; + } + } else { + prev = item; + item = item->next; + } + } } -#endif 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 LINUX_JoystickInit(void) { @@ -433,10 +470,24 @@ LINUX_JoystickInit(void) SteamControllerDisconnectedCallback); #if SDL_USE_LIBUDEV - return JoystickInitWithUdev(); -#else - return JoystickInitWithoutUdev(); + 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 + /* Report all devices currently present */ + LINUX_JoystickDetect(); #endif + + return 0; } static int @@ -445,16 +496,6 @@ LINUX_JoystickGetCount(void) return numjoysticks; } -static void -LINUX_JoystickDetect(void) -{ -#if SDL_USE_LIBUDEV - SDL_UDEV_Poll(); -#endif - - SDL_UpdateSteamControllers(); -} - static SDL_joylist_item * 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 diff --git a/src/joystick/linux/SDL_sysjoystick_c.h b/src/joystick/linux/SDL_sysjoystick_c.h index ef50730a89fbd..f50f2f6e337d7 100644 --- a/src/joystick/linux/SDL_sysjoystick_c.h +++ b/src/joystick/linux/SDL_sysjoystick_c.h @@ -64,6 +64,9 @@ struct joystick_hwdata SDL_bool m_bSteamController; /* 4 = (ABS_HAT3X-ABS_HAT0X)/2 (see input-event-codes.h in kernel) */ int hats_indices[4]; + + /* Set when gamepad is pending removal due to ENODEV read error */ + SDL_bool gone; }; #endif /* SDL_sysjoystick_c_h_ */