Allow hotplugging joysticks without udev
authorCameron Gutman <cameron.gutman@gmail.com>
Mon, 24 Jun 2019 21:08:26 -0700
changeset 12944886b79177762
parent 12943 49190e92b7d1
child 12945 36035e851264
Allow hotplugging joysticks without udev
src/joystick/linux/SDL_sysjoystick.c
src/joystick/linux/SDL_sysjoystick_c.h
     1.1 --- a/src/joystick/linux/SDL_sysjoystick.c	Mon Jul 15 09:36:53 2019 -0700
     1.2 +++ b/src/joystick/linux/SDL_sysjoystick.c	Mon Jun 24 21:08:26 2019 -0700
     1.3 @@ -34,11 +34,13 @@
     1.4  #include <limits.h>             /* For the definition of PATH_MAX */
     1.5  #include <sys/ioctl.h>
     1.6  #include <unistd.h>
     1.7 +#include <dirent.h>
     1.8  #include <linux/joystick.h>
     1.9  
    1.10  #include "SDL_assert.h"
    1.11  #include "SDL_joystick.h"
    1.12  #include "SDL_endian.h"
    1.13 +#include "SDL_timer.h"
    1.14  #include "../../events/SDL_events_c.h"
    1.15  #include "../SDL_sysjoystick.h"
    1.16  #include "../SDL_joystick_c.h"
    1.17 @@ -56,10 +58,8 @@
    1.18  static int MaybeAddDevice(const char *path);
    1.19  #if SDL_USE_LIBUDEV
    1.20  static int MaybeRemoveDevice(const char *path);
    1.21 -static void joystick_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath);
    1.22  #endif /* SDL_USE_LIBUDEV */
    1.23  
    1.24 -
    1.25  /* A linked list of available joysticks */
    1.26  typedef struct SDL_joylist_item
    1.27  {
    1.28 @@ -79,6 +79,9 @@
    1.29  static SDL_joylist_item *SDL_joylist_tail = NULL;
    1.30  static int numjoysticks = 0;
    1.31  
    1.32 +#if !SDL_USE_LIBUDEV
    1.33 +static Uint32 last_joy_detect_time = 0;
    1.34 +#endif
    1.35  
    1.36  #define test_bit(nr, addr) \
    1.37      (((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0)
    1.38 @@ -177,8 +180,6 @@
    1.39  }
    1.40  #endif /* SDL_USE_LIBUDEV */
    1.41  
    1.42 -
    1.43 -/* !!! FIXME: I would love to dump this code and use libudev instead. */
    1.44  static int
    1.45  MaybeAddDevice(const char *path)
    1.46  {
    1.47 @@ -254,7 +255,6 @@
    1.48  }
    1.49  
    1.50  #if SDL_USE_LIBUDEV
    1.51 -/* !!! FIXME: I would love to dump this code and use libudev instead. */
    1.52  static int
    1.53  MaybeRemoveDevice(const char *path)
    1.54  {
    1.55 @@ -299,45 +299,46 @@
    1.56  }
    1.57  #endif
    1.58  
    1.59 -#if ! SDL_USE_LIBUDEV
    1.60 -static int
    1.61 -JoystickInitWithoutUdev(void)
    1.62 +static void
    1.63 +HandlePendingRemovals(void)
    1.64  {
    1.65 -    int i;
    1.66 -    char path[PATH_MAX];
    1.67 +    SDL_joylist_item *prev = NULL;
    1.68 +    SDL_joylist_item *item = SDL_joylist;
    1.69  
    1.70 -    /* !!! FIXME: only finds sticks if they're called /dev/input/event[0..31] */
    1.71 -    /* !!! FIXME:  we could at least readdir() through /dev/input...? */
    1.72 -    /* !!! FIXME:  (or delete this and rely on libudev?) */
    1.73 -    for (i = 0; i < 32; i++) {
    1.74 -        SDL_snprintf(path, SDL_arraysize(path), "/dev/input/event%d", i);
    1.75 -        MaybeAddDevice(path);
    1.76 +    while (item != NULL) {
    1.77 +        if (item->hwdata && item->hwdata->gone) {
    1.78 +            item->hwdata->item = NULL;
    1.79 +
    1.80 +            if (prev != NULL) {
    1.81 +                prev->next = item->next;
    1.82 +            } else {
    1.83 +                SDL_assert(SDL_joylist == item);
    1.84 +                SDL_joylist = item->next;
    1.85 +            }
    1.86 +            if (item == SDL_joylist_tail) {
    1.87 +                SDL_joylist_tail = prev;
    1.88 +            }
    1.89 +
    1.90 +            /* Need to decrement the joystick count before we post the event */
    1.91 +            --numjoysticks;
    1.92 +
    1.93 +            SDL_PrivateJoystickRemoved(item->device_instance);
    1.94 +
    1.95 +            SDL_free(item->path);
    1.96 +            SDL_free(item->name);
    1.97 +            SDL_free(item);
    1.98 +
    1.99 +            if (prev != NULL) {
   1.100 +                item = prev->next;
   1.101 +            } else {
   1.102 +                item = SDL_joylist;
   1.103 +            }
   1.104 +        } else {
   1.105 +            prev = item;
   1.106 +            item = item->next;
   1.107 +        }
   1.108      }
   1.109 -
   1.110 -    return 0;
   1.111  }
   1.112 -#endif
   1.113 -
   1.114 -#if SDL_USE_LIBUDEV
   1.115 -static int
   1.116 -JoystickInitWithUdev(void)
   1.117 -{
   1.118 -    if (SDL_UDEV_Init() < 0) {
   1.119 -        return SDL_SetError("Could not initialize UDEV");
   1.120 -    }
   1.121 -
   1.122 -    /* Set up the udev callback */
   1.123 -    if (SDL_UDEV_AddCallback(joystick_udev_callback) < 0) {
   1.124 -        SDL_UDEV_Quit();
   1.125 -        return SDL_SetError("Could not set up joystick <-> udev callback");
   1.126 -    }
   1.127 -    
   1.128 -    /* Force a scan to build the initial device list */
   1.129 -    SDL_UDEV_Scan();
   1.130 -
   1.131 -    return 0;
   1.132 -}
   1.133 -#endif
   1.134  
   1.135  static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickGUID guid, int *device_instance)
   1.136  {
   1.137 @@ -410,6 +411,42 @@
   1.138      }
   1.139  }
   1.140  
   1.141 +static void
   1.142 +LINUX_JoystickDetect(void)
   1.143 +{
   1.144 +#if SDL_USE_LIBUDEV
   1.145 +    SDL_UDEV_Poll();
   1.146 +#else
   1.147 +    const Uint32 SDL_JOY_DETECT_INTERVAL_MS = 3000;  /* Update every 3 seconds */
   1.148 +    Uint32 now = SDL_GetTicks();
   1.149 +
   1.150 +    if (!last_joy_detect_time || SDL_TICKS_PASSED(now, last_joy_detect_time + SDL_JOY_DETECT_INTERVAL_MS)) {
   1.151 +        DIR *folder;
   1.152 +        struct dirent *dent;
   1.153 +
   1.154 +        folder = opendir("/dev/input");
   1.155 +        if (folder) {
   1.156 +            while ((dent = readdir(folder))) {
   1.157 +                int len = SDL_strlen(dent->d_name);
   1.158 +                if (len > 5 && SDL_strncmp(dent->d_name, "event", 5) == 0) {
   1.159 +                    char path[PATH_MAX];
   1.160 +                    SDL_snprintf(path, SDL_arraysize(path), "/dev/input/%s", dent->d_name);
   1.161 +                    MaybeAddDevice(path);
   1.162 +                }
   1.163 +            }
   1.164 +
   1.165 +            closedir(folder);
   1.166 +        }
   1.167 +
   1.168 +        last_joy_detect_time = now;
   1.169 +    }
   1.170 +#endif
   1.171 +
   1.172 +    HandlePendingRemovals();
   1.173 +
   1.174 +    SDL_UpdateSteamControllers();
   1.175 +}
   1.176 +
   1.177  static int
   1.178  LINUX_JoystickInit(void)
   1.179  {
   1.180 @@ -433,10 +470,24 @@
   1.181                               SteamControllerDisconnectedCallback);
   1.182  
   1.183  #if SDL_USE_LIBUDEV
   1.184 -    return JoystickInitWithUdev();
   1.185 -#else 
   1.186 -    return JoystickInitWithoutUdev();
   1.187 +    if (SDL_UDEV_Init() < 0) {
   1.188 +        return SDL_SetError("Could not initialize UDEV");
   1.189 +    }
   1.190 +
   1.191 +    /* Set up the udev callback */
   1.192 +    if (SDL_UDEV_AddCallback(joystick_udev_callback) < 0) {
   1.193 +        SDL_UDEV_Quit();
   1.194 +        return SDL_SetError("Could not set up joystick <-> udev callback");
   1.195 +    }
   1.196 +
   1.197 +    /* Force a scan to build the initial device list */
   1.198 +    SDL_UDEV_Scan();
   1.199 +#else
   1.200 +    /* Report all devices currently present */
   1.201 +    LINUX_JoystickDetect();
   1.202  #endif
   1.203 +
   1.204 +    return 0;
   1.205  }
   1.206  
   1.207  static int
   1.208 @@ -445,16 +496,6 @@
   1.209      return numjoysticks;
   1.210  }
   1.211  
   1.212 -static void
   1.213 -LINUX_JoystickDetect(void)
   1.214 -{
   1.215 -#if SDL_USE_LIBUDEV
   1.216 -    SDL_UDEV_Poll();
   1.217 -#endif
   1.218 -
   1.219 -    SDL_UpdateSteamControllers();
   1.220 -}
   1.221 -
   1.222  static SDL_joylist_item *
   1.223  JoystickByDevIndex(int device_index)
   1.224  {
   1.225 @@ -915,6 +956,11 @@
   1.226              }
   1.227          }
   1.228      }
   1.229 +
   1.230 +    if (errno == ENODEV) {
   1.231 +        /* We have to wait until the JoystickDetect callback to remove this */
   1.232 +        joystick->hwdata->gone = SDL_TRUE;
   1.233 +    }
   1.234  }
   1.235  
   1.236  static void
     2.1 --- a/src/joystick/linux/SDL_sysjoystick_c.h	Mon Jul 15 09:36:53 2019 -0700
     2.2 +++ b/src/joystick/linux/SDL_sysjoystick_c.h	Mon Jun 24 21:08:26 2019 -0700
     2.3 @@ -64,6 +64,9 @@
     2.4      SDL_bool m_bSteamController;
     2.5      /* 4 = (ABS_HAT3X-ABS_HAT0X)/2 (see input-event-codes.h in kernel) */
     2.6      int hats_indices[4];
     2.7 +
     2.8 +    /* Set when gamepad is pending removal due to ENODEV read error */
     2.9 +    SDL_bool gone;
    2.10  };
    2.11  
    2.12  #endif /* SDL_sysjoystick_c_h_ */