Updated Linux joystick code to support hotplug, GUIDs, etc.
authorRyan C. Gordon <icculus@icculus.org>
Tue, 11 Dec 2012 12:07:06 -0500
changeset 67347fdaee2be782
parent 6733 cfef740efada
child 6735 486800c3f44c
Updated Linux joystick code to support hotplug, GUIDs, etc.

This uses libudev for hotplug, but it's optional, so we'll just try to find
some reasonable defaults without it (maybe an older Linux box or under
FreeBSD's Linux emulation?).
CMakeLists.txt
configure.in
include/SDL_config.h.cmake
include/SDL_config.h.in
src/joystick/linux/SDL_sysjoystick.c
src/joystick/linux/SDL_sysjoystick_c.h
     1.1 --- a/CMakeLists.txt	Tue Dec 11 11:59:29 2012 -0500
     1.2 +++ b/CMakeLists.txt	Tue Dec 11 12:07:06 2012 -0500
     1.3 @@ -648,7 +648,9 @@
     1.4        set(SOURCE_FILES ${SOURCE_FILES} ${HAPTIC_SOURCES})
     1.5        set(HAVE_SDL_HAPTIC TRUE)
     1.6      endif(SDL_HAPTIC AND HAVE_INPUT_EVENTS)
     1.7 -  endif(LINUX
     1.8 +
     1.9 +    check_include_file("libudev.h" HAVE_LIBUDEV_H)
    1.10 +  endif(LINUX)
    1.11  
    1.12    if(INPUT_TSLIB)
    1.13      check_c_source_compiles("
     2.1 --- a/configure.in	Tue Dec 11 11:59:29 2012 -0500
     2.2 +++ b/configure.in	Tue Dec 11 12:07:06 2012 -0500
     2.3 @@ -1667,6 +1667,22 @@
     2.4          fi
     2.5  }
     2.6  
     2.7 +dnl See if the platform offers libudev for device enumeration and hotplugging.
     2.8 +CheckLibUDev()
     2.9 +{
    2.10 +    AC_ARG_ENABLE(libudev,
    2.11 +AC_HELP_STRING([--enable-libudev], [enable libudev support [[default=yes]]]),
    2.12 +                        , enable_libudev=yes)
    2.13 +    if test x$enable_libudev = xyes; then
    2.14 +        AC_CHECK_HEADER(libudev.h,
    2.15 +                        have_libudev_h_hdr=yes,
    2.16 +                        have_libudev_h_hdr=no)
    2.17 +        if test x$have_libudev_h_hdr = xyes; then
    2.18 +            AC_DEFINE(HAVE_LIBUDEV_H, 1, [ ])
    2.19 +        fi
    2.20 +    fi
    2.21 +}
    2.22 +
    2.23  dnl See if we can use the Touchscreen input library
    2.24  CheckTslib()
    2.25  {
    2.26 @@ -2191,6 +2207,7 @@
    2.27          CheckFusionSound
    2.28          CheckOpenGLX11
    2.29          CheckOpenGLESX11
    2.30 +        CheckLibUDev
    2.31          CheckInputEvents
    2.32          CheckTslib
    2.33          CheckUSBHID
     3.1 --- a/include/SDL_config.h.cmake	Tue Dec 11 11:59:29 2012 -0500
     3.2 +++ b/include/SDL_config.h.cmake	Tue Dec 11 12:07:06 2012 -0500
     3.3 @@ -65,6 +65,7 @@
     3.4  #cmakedefine HAVE_SIGNAL_H 1
     3.5  #cmakedefine HAVE_ALTIVEC_H 1
     3.6  #cmakedefine HAVE_PTHREAD_NP_H 1
     3.7 +#cmakedefine HAVE_LIBUDEV_H 1
     3.8  
     3.9  /* C library functions */
    3.10  #cmakedefine HAVE_MALLOC 1
     4.1 --- a/include/SDL_config.h.in	Tue Dec 11 11:59:29 2012 -0500
     4.2 +++ b/include/SDL_config.h.in	Tue Dec 11 12:07:06 2012 -0500
     4.3 @@ -70,6 +70,7 @@
     4.4  #undef HAVE_SIGNAL_H
     4.5  #undef HAVE_ALTIVEC_H
     4.6  #undef HAVE_PTHREAD_NP_H
     4.7 +#undef HAVE_LIBUDEV_H
     4.8  
     4.9  /* C library functions */
    4.10  #undef HAVE_MALLOC
     5.1 --- a/src/joystick/linux/SDL_sysjoystick.c	Tue Dec 11 11:59:29 2012 -0500
     5.2 +++ b/src/joystick/linux/SDL_sysjoystick.c	Tue Dec 11 12:07:06 2012 -0500
     5.3 @@ -35,31 +35,168 @@
     5.4  #include <limits.h>             /* For the definition of PATH_MAX */
     5.5  #include <linux/joystick.h>
     5.6  
     5.7 +#include "SDL_assert.h"
     5.8  #include "SDL_joystick.h"
     5.9 +#include "SDL_endian.h"
    5.10  #include "../SDL_sysjoystick.h"
    5.11  #include "../SDL_joystick_c.h"
    5.12  #include "SDL_sysjoystick_c.h"
    5.13  
    5.14 -/* The maximum number of joysticks we'll detect */
    5.15 -#define MAX_JOYSTICKS	32
    5.16 +/* !!! FIXME: move this somewhere else. */
    5.17 +#if !SDL_EVENTS_DISABLED
    5.18 +#include "../../events/SDL_events_c.h"
    5.19 +#endif
    5.20  
    5.21 -/* A list of available joysticks */
    5.22 -static struct
    5.23 +/*
    5.24 + * !!! FIXME: move all the udev stuff to src/core/linux, so I can reuse it
    5.25 + * !!! FIXME:  for audio hardware disconnects.
    5.26 + */
    5.27 +#ifdef HAVE_LIBUDEV_H
    5.28 +#define SDL_USE_LIBUDEV 1
    5.29 +#include "SDL_loadso.h"
    5.30 +#include <libudev.h>
    5.31 +#include <sys/time.h>
    5.32 +#include <sys/types.h>
    5.33 +#include <unistd.h>
    5.34 +
    5.35 +/* we never link directly to libudev. */
    5.36 +/* !!! FIXME: can we generalize this? ALSA, etc, do the same things. */
    5.37 +static const char *udev_library = "libudev.so";
    5.38 +static void *udev_handle = NULL;
    5.39 +
    5.40 +/* !!! FIXME: this is kinda ugly. */
    5.41 +static SDL_bool
    5.42 +load_udev_sym(const char *fn, void **addr)
    5.43  {
    5.44 -    char *fname;
    5.45 -} SDL_joylist[MAX_JOYSTICKS];
    5.46 +    *addr = SDL_LoadFunction(udev_handle, fn);
    5.47 +    if (*addr == NULL) {
    5.48 +        /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
    5.49 +        return SDL_FALSE;
    5.50 +    }
    5.51  
    5.52 +    return SDL_TRUE;
    5.53 +}
    5.54 +
    5.55 +/* libudev entry points... */
    5.56 +static const char *(*UDEV_udev_device_get_action)(struct udev_device *) = NULL;
    5.57 +static const char *(*UDEV_udev_device_get_devnode)(struct udev_device *) = NULL;
    5.58 +static const char *(*UDEV_udev_device_get_property_value)(struct udev_device *, const char *) = NULL;
    5.59 +static struct udev_device *(*UDEV_udev_device_new_from_syspath)(struct udev *, const char *) = NULL;
    5.60 +static void (*UDEV_udev_device_unref)(struct udev_device *) = NULL;
    5.61 +static int (*UDEV_udev_enumerate_add_match_property)(struct udev_enumerate *, const char *, const char *) = NULL;
    5.62 +static int (*UDEV_udev_enumerate_add_match_subsystem)(struct udev_enumerate *, const char *) = NULL;
    5.63 +static struct udev_list_entry *(*UDEV_udev_enumerate_get_list_entry)(struct udev_enumerate *) = NULL;
    5.64 +static struct udev_enumerate *(*UDEV_udev_enumerate_new)(struct udev *) = NULL;
    5.65 +static int (*UDEV_udev_enumerate_scan_devices)(struct udev_enumerate *) = NULL;
    5.66 +static void (*UDEV_udev_enumerate_unref)(struct udev_enumerate *) = NULL;
    5.67 +static const char *(*UDEV_udev_list_entry_get_name)(struct udev_list_entry *) = NULL;
    5.68 +static struct udev_list_entry *(*UDEV_udev_list_entry_get_next)(struct udev_list_entry *) = NULL;
    5.69 +static int (*UDEV_udev_monitor_enable_receiving)(struct udev_monitor *) = NULL;
    5.70 +static int (*UDEV_udev_monitor_filter_add_match_subsystem_devtype)(struct udev_monitor *, const char *, const char *) = NULL;
    5.71 +static int (*UDEV_udev_monitor_get_fd)(struct udev_monitor *) = NULL;
    5.72 +static struct udev_monitor *(*UDEV_udev_monitor_new_from_netlink)(struct udev *, const char *) = NULL;
    5.73 +static struct udev_device *(*UDEV_udev_monitor_receive_device)(struct udev_monitor *) = NULL;
    5.74 +static void (*UDEV_udev_monitor_unref)(struct udev_monitor *) = NULL;
    5.75 +static struct udev *(*UDEV_udev_new)(void) = NULL;
    5.76 +static void (*UDEV_udev_unref)(struct udev *) = NULL;
    5.77 +
    5.78 +static int
    5.79 +load_udev_syms(void)
    5.80 +{
    5.81 +    /* cast funcs to char* first, to please GCC's strict aliasing rules. */
    5.82 +    #define SDL_UDEV_SYM(x) \
    5.83 +        if (!load_udev_sym(#x, (void **) (char *) &UDEV_##x)) return -1
    5.84 +
    5.85 +    SDL_UDEV_SYM(udev_device_get_action);
    5.86 +    SDL_UDEV_SYM(udev_device_get_devnode);
    5.87 +    SDL_UDEV_SYM(udev_device_get_property_value);
    5.88 +    SDL_UDEV_SYM(udev_device_new_from_syspath);
    5.89 +    SDL_UDEV_SYM(udev_device_unref);
    5.90 +    SDL_UDEV_SYM(udev_enumerate_add_match_property);
    5.91 +    SDL_UDEV_SYM(udev_enumerate_add_match_subsystem);
    5.92 +    SDL_UDEV_SYM(udev_enumerate_get_list_entry);
    5.93 +    SDL_UDEV_SYM(udev_enumerate_new);
    5.94 +    SDL_UDEV_SYM(udev_enumerate_scan_devices);
    5.95 +    SDL_UDEV_SYM(udev_enumerate_unref);
    5.96 +    SDL_UDEV_SYM(udev_list_entry_get_name);
    5.97 +    SDL_UDEV_SYM(udev_list_entry_get_next);
    5.98 +    SDL_UDEV_SYM(udev_monitor_enable_receiving);
    5.99 +    SDL_UDEV_SYM(udev_monitor_filter_add_match_subsystem_devtype);
   5.100 +    SDL_UDEV_SYM(udev_monitor_get_fd);
   5.101 +    SDL_UDEV_SYM(udev_monitor_new_from_netlink);
   5.102 +    SDL_UDEV_SYM(udev_monitor_receive_device);
   5.103 +    SDL_UDEV_SYM(udev_monitor_unref);
   5.104 +    SDL_UDEV_SYM(udev_new);
   5.105 +    SDL_UDEV_SYM(udev_unref);
   5.106 +
   5.107 +    #undef SDL_UDEV_SYM
   5.108 +
   5.109 +    return 0;
   5.110 +}
   5.111 +
   5.112 +static void
   5.113 +UnloadUDEVLibrary(void)
   5.114 +{
   5.115 +    if (udev_handle != NULL) {
   5.116 +        SDL_UnloadObject(udev_handle);
   5.117 +        udev_handle = NULL;
   5.118 +    }
   5.119 +}
   5.120 +
   5.121 +static int
   5.122 +LoadUDEVLibrary(void)
   5.123 +{
   5.124 +    int retval = 0;
   5.125 +    if (udev_handle == NULL) {
   5.126 +        udev_handle = SDL_LoadObject(udev_library);
   5.127 +        if (udev_handle == NULL) {
   5.128 +            retval = -1;
   5.129 +            /* Don't call SDL_SetError(): SDL_LoadObject already did. */
   5.130 +        } else {
   5.131 +            retval = load_udev_syms();
   5.132 +            if (retval < 0) {
   5.133 +                UnloadUDEVLibrary();
   5.134 +            }
   5.135 +        }
   5.136 +    }
   5.137 +
   5.138 +    return retval;
   5.139 +}
   5.140 +
   5.141 +static struct udev *udev = NULL;
   5.142 +static struct udev_monitor *udev_mon = NULL;
   5.143 +#endif
   5.144 +
   5.145 +
   5.146 +/* A linked list of available joysticks */
   5.147 +typedef struct SDL_joylist_item
   5.148 +{
   5.149 +    int device_instance;
   5.150 +    char *path;   /* "/dev/input/event2" or whatever */
   5.151 +    char *name;   /* "SideWinder 3D Pro" or whatever */
   5.152 +    JoystickGUID guid;
   5.153 +    dev_t devnum;
   5.154 +    struct joystick_hwdata *hwdata;
   5.155 +    struct SDL_joylist_item *next;
   5.156 +} SDL_joylist_item;
   5.157 +
   5.158 +static SDL_joylist_item *SDL_joylist = NULL;
   5.159 +static SDL_joylist_item *SDL_joylist_tail = NULL;
   5.160 +static int numjoysticks = 0;
   5.161 +static int instance_counter = 0;
   5.162  
   5.163  #define test_bit(nr, addr) \
   5.164 -	(((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0)
   5.165 +    (((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0)
   5.166  #define NBITS(x) ((((x)-1)/(sizeof(long) * 8))+1)
   5.167  
   5.168  static int
   5.169 -IsJoystick(int fd)
   5.170 +IsJoystick(int fd, char *namebuf, const size_t namebuflen, JoystickGUID *guid)
   5.171  {
   5.172      unsigned long evbit[NBITS(EV_MAX)] = { 0 };
   5.173      unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
   5.174      unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
   5.175 +    struct input_id inpid;
   5.176 +    Uint16 *guid16 = (Uint16 *) ((char *) &guid->data);
   5.177  
   5.178      if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) ||
   5.179          (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) ||
   5.180 @@ -71,30 +208,206 @@
   5.181            test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit))) {
   5.182          return 0;
   5.183      }
   5.184 -    return (1);
   5.185 +
   5.186 +    if (ioctl(fd, EVIOCGNAME(namebuflen), namebuf) < 0) {
   5.187 +        return 0;
   5.188 +    }
   5.189 +
   5.190 +    if (ioctl(fd, EVIOCGID, &inpid) < 0) {
   5.191 +        return 0;
   5.192 +    }
   5.193 +
   5.194 +    /* We only need 16 bits for each of these; space them out to fill 128. */
   5.195 +    /* Byteswap so devices get same GUID on little/big endian platforms. */
   5.196 +    *(guid16++) = SDL_SwapLE16(inpid.bustype);
   5.197 +    *(guid16++) = 0;
   5.198 +    *(guid16++) = SDL_SwapLE16(inpid.vendor);
   5.199 +    *(guid16++) = 0;
   5.200 +    *(guid16++) = SDL_SwapLE16(inpid.product);
   5.201 +    *(guid16++) = 0;
   5.202 +    *(guid16++) = SDL_SwapLE16(inpid.version);
   5.203 +    *(guid16++) = 0;
   5.204 +
   5.205 +    return 1;
   5.206  }
   5.207  
   5.208  
   5.209 -static int SDL_SYS_numjoysticks = 0;
   5.210 +/* !!! FIXME: I would love to dump this code and use libudev instead. */
   5.211 +static int
   5.212 +MaybeAddDevice(const char *path)
   5.213 +{
   5.214 +    struct stat sb;
   5.215 +    int fd = -1;
   5.216 +    int isstick = 0;
   5.217 +    char namebuf[128];
   5.218 +    JoystickGUID guid;
   5.219 +    SDL_joylist_item *item;
   5.220  
   5.221 -/* Function to scan the system for joysticks */
   5.222 +    if (path == NULL) {
   5.223 +        return -1;
   5.224 +    }
   5.225 +
   5.226 +    if (stat(path, &sb) == -1) {
   5.227 +        return -1;
   5.228 +    }
   5.229 +
   5.230 +    /* Check to make sure it's not already in list. */
   5.231 +    for (item = SDL_joylist; item != NULL; item = item->next) {
   5.232 +        if (sb.st_rdev == item->devnum) {
   5.233 +            return -1;  /* already have this one */
   5.234 +        }
   5.235 +    }
   5.236 +
   5.237 +    fd = open(path, O_RDONLY, 0);
   5.238 +    if (fd < 0) {
   5.239 +        return -1;
   5.240 +    }
   5.241 +
   5.242 +#ifdef DEBUG_INPUT_EVENTS
   5.243 +    printf("Checking %s\n", path);
   5.244 +#endif
   5.245 +
   5.246 +    isstick = IsJoystick(fd, namebuf, sizeof (namebuf), &guid);
   5.247 +    close(fd);
   5.248 +    if (!isstick) {
   5.249 +        return -1;
   5.250 +    }
   5.251 +
   5.252 +    item = (SDL_joylist_item *) SDL_malloc(sizeof (SDL_joylist_item));
   5.253 +    if (item == NULL) {
   5.254 +        return -1;
   5.255 +    }
   5.256 +
   5.257 +    SDL_zerop(item);
   5.258 +    item->devnum = sb.st_rdev;
   5.259 +    item->path = SDL_strdup(path);
   5.260 +    item->name = SDL_strdup(namebuf);
   5.261 +    item->guid = guid;
   5.262 +
   5.263 +    if ( (item->path == NULL) || (item->name == NULL) ) {
   5.264 +         SDL_free(item->path);
   5.265 +         SDL_free(item->name);
   5.266 +         SDL_free(item);
   5.267 +         return -1;
   5.268 +    }
   5.269 +
   5.270 +    item->device_instance = instance_counter++;
   5.271 +    if (SDL_joylist_tail == NULL) {
   5.272 +        SDL_joylist = SDL_joylist_tail = item;
   5.273 +    } else {
   5.274 +        SDL_joylist_tail->next = item;
   5.275 +        SDL_joylist_tail = item;
   5.276 +    }
   5.277 +
   5.278 +    return numjoysticks++;
   5.279 +}
   5.280 +
   5.281 +/* !!! FIXME: I would love to dump this code and use libudev instead. */
   5.282 +static int
   5.283 +MaybeRemoveDevice(const char *path)
   5.284 +{
   5.285 +    SDL_joylist_item *item;
   5.286 +    SDL_joylist_item *prev = NULL;
   5.287 +
   5.288 +    if (path == NULL) {
   5.289 +        return -1;
   5.290 +    }
   5.291 +
   5.292 +    for (item = SDL_joylist; item != NULL; item = item->next) {
   5.293 +        /* found it, remove it. */
   5.294 +        if (SDL_strcmp(path, item->path) == 0) {
   5.295 +            const int retval = item->device_instance;
   5.296 +            if (item->hwdata) {
   5.297 +                item->hwdata->removed = SDL_TRUE;
   5.298 +            }
   5.299 +            if (prev != NULL) {
   5.300 +                prev->next = item->next;
   5.301 +                if (item == SDL_joylist_tail) {
   5.302 +                    SDL_joylist_tail = prev;
   5.303 +                }
   5.304 +            } else {
   5.305 +                SDL_assert(!SDL_joylist);
   5.306 +                SDL_assert(!SDL_joylist_tail);
   5.307 +                SDL_joylist = SDL_joylist_tail = NULL;
   5.308 +            }
   5.309 +            SDL_free(item->path);
   5.310 +            SDL_free(item->name);
   5.311 +            SDL_free(item);
   5.312 +            numjoysticks--;
   5.313 +            return retval;
   5.314 +        }
   5.315 +        prev = item;
   5.316 +    }
   5.317 +
   5.318 +    return -1;
   5.319 +}
   5.320 +
   5.321 +static int
   5.322 +JoystickInitWithoutUdev(void)
   5.323 +{
   5.324 +    int i;
   5.325 +    char path[PATH_MAX];
   5.326 +
   5.327 +    /* !!! FIXME: only finds sticks if they're called /dev/input/event[0..31] */
   5.328 +    /* !!! FIXME:  we could at least readdir() through /dev/input...? */
   5.329 +    /* !!! FIXME:  (or delete this and rely on libudev?) */
   5.330 +    for (i = 0; i < 32; i++) {
   5.331 +        SDL_snprintf(path, SDL_arraysize(path), "/dev/input/event%d", i);
   5.332 +        MaybeAddDevice(path);
   5.333 +    }
   5.334 +
   5.335 +    return numjoysticks;
   5.336 +}
   5.337 +
   5.338 +
   5.339 +#if SDL_USE_LIBUDEV
   5.340 +static int
   5.341 +JoystickInitWithUdev(void)
   5.342 +{
   5.343 +    struct udev_enumerate *enumerate = NULL;
   5.344 +    struct udev_list_entry *devs = NULL;
   5.345 +    struct udev_list_entry *item = NULL;
   5.346 +
   5.347 +    SDL_assert(udev == NULL);
   5.348 +    udev = UDEV_udev_new();
   5.349 +    if (udev == NULL) {
   5.350 +        SDL_SetError("udev_new() failed");
   5.351 +        return -1;
   5.352 +    }
   5.353 +
   5.354 +    udev_mon = UDEV_udev_monitor_new_from_netlink(udev, "udev");
   5.355 +    if (udev_mon != NULL) {  /* okay if it's NULL, we just lose hotplugging. */
   5.356 +        UDEV_udev_monitor_filter_add_match_subsystem_devtype(udev_mon,
   5.357 +                                                             "input", NULL);
   5.358 +        UDEV_udev_monitor_enable_receiving(udev_mon);
   5.359 +    }
   5.360 +
   5.361 +    enumerate = UDEV_udev_enumerate_new(udev);
   5.362 +    if (enumerate == NULL) {
   5.363 +        SDL_SetError("udev_enumerate_new() failed");
   5.364 +        return -1;
   5.365 +    }
   5.366 +
   5.367 +    UDEV_udev_enumerate_add_match_subsystem(enumerate, "input");
   5.368 +    UDEV_udev_enumerate_add_match_property(enumerate, "ID_INPUT_JOYSTICK", "1");
   5.369 +    UDEV_udev_enumerate_scan_devices(enumerate);
   5.370 +    devs = UDEV_udev_enumerate_get_list_entry(enumerate);
   5.371 +    for (item = devs; item; item = UDEV_udev_list_entry_get_next(item)) {
   5.372 +        const char *path = UDEV_udev_list_entry_get_name(item);
   5.373 +        struct udev_device *dev = UDEV_udev_device_new_from_syspath(udev, path);
   5.374 +        MaybeAddDevice(UDEV_udev_device_get_devnode(dev));
   5.375 +        UDEV_udev_device_unref(dev);
   5.376 +    }
   5.377 +
   5.378 +    UDEV_udev_enumerate_unref(enumerate);
   5.379 +
   5.380 +    return numjoysticks;
   5.381 +}
   5.382 +#endif
   5.383 +
   5.384  int
   5.385  SDL_SYS_JoystickInit(void)
   5.386  {
   5.387 -    /* The base path of the joystick devices */
   5.388 -    const char *joydev_pattern[] = {
   5.389 -        "/dev/input/event%d",
   5.390 -    };
   5.391 -    int numjoysticks;
   5.392 -    int i, j;
   5.393 -    int fd;
   5.394 -    char path[PATH_MAX];
   5.395 -    dev_t dev_nums[MAX_JOYSTICKS];      /* major/minor device numbers */
   5.396 -    struct stat sb;
   5.397 -    int n, duplicate;
   5.398 -
   5.399 -    numjoysticks = 0;
   5.400 -
   5.401      /* First see if the user specified one or more joysticks to use */
   5.402      if (SDL_getenv("SDL_JOYSTICK_DEVICE") != NULL) {
   5.403          char *envcopy, *envpath, *delim;
   5.404 @@ -105,121 +418,150 @@
   5.405              if (delim != NULL) {
   5.406                  *delim++ = '\0';
   5.407              }
   5.408 -            if (stat(envpath, &sb) == 0) {
   5.409 -                fd = open(envpath, O_RDONLY, 0);
   5.410 -                if (fd >= 0) {
   5.411 -                    /* Assume the user knows what they're doing. */
   5.412 -                    SDL_joylist[numjoysticks].fname = SDL_strdup(envpath);
   5.413 -                    if (SDL_joylist[numjoysticks].fname) {
   5.414 -                        dev_nums[numjoysticks] = sb.st_rdev;
   5.415 -                        ++numjoysticks;
   5.416 -                    }
   5.417 -                    close(fd);
   5.418 -                }
   5.419 -            }
   5.420 +            MaybeAddDevice(envpath);
   5.421              envpath = delim;
   5.422          }
   5.423          SDL_free(envcopy);
   5.424      }
   5.425  
   5.426 -    for (i = 0; i < SDL_arraysize(joydev_pattern); ++i) {
   5.427 -        for (j = 0; j < MAX_JOYSTICKS; ++j) {
   5.428 -            SDL_snprintf(path, SDL_arraysize(path), joydev_pattern[i], j);
   5.429 +#if SDL_USE_LIBUDEV
   5.430 +    if (LoadUDEVLibrary() == 0) {   /* okay if this fails, FOR NOW. */
   5.431 +        return JoystickInitWithUdev();
   5.432 +    }
   5.433 +#endif
   5.434  
   5.435 -            /* rcg06302000 replaced access(F_OK) call with stat().
   5.436 -             * stat() will fail if the file doesn't exist, so it's
   5.437 -             * equivalent behaviour.
   5.438 -             */
   5.439 -            if (stat(path, &sb) == 0) {
   5.440 -                /* Check to make sure it's not already in list.
   5.441 -                 * This happens when we see a stick via symlink.
   5.442 -                 */
   5.443 -                duplicate = 0;
   5.444 -                for (n = 0; (n < numjoysticks) && !duplicate; ++n) {
   5.445 -                    if (sb.st_rdev == dev_nums[n]) {
   5.446 -                        duplicate = 1;
   5.447 -                    }
   5.448 -                }
   5.449 -                if (duplicate) {
   5.450 -                    continue;
   5.451 -                }
   5.452 -
   5.453 -                fd = open(path, O_RDONLY, 0);
   5.454 -                if (fd < 0) {
   5.455 -                    continue;
   5.456 -                }
   5.457 -#ifdef DEBUG_INPUT_EVENTS
   5.458 -                printf("Checking %s\n", path);
   5.459 -#endif
   5.460 -                if ((i == 0) && !IsJoystick(fd)) {
   5.461 -                    close(fd);
   5.462 -                    continue;
   5.463 -                }
   5.464 -                close(fd);
   5.465 -
   5.466 -                /* We're fine, add this joystick */
   5.467 -                SDL_joylist[numjoysticks].fname = SDL_strdup(path);
   5.468 -                if (SDL_joylist[numjoysticks].fname) {
   5.469 -                    dev_nums[numjoysticks] = sb.st_rdev;
   5.470 -                    ++numjoysticks;
   5.471 -                }
   5.472 -            }
   5.473 -        }
   5.474 -
   5.475 -        /* This is a special case...
   5.476 -           If the event devices are valid then the joystick devices
   5.477 -           will be duplicates but without extra information about their
   5.478 -           hats or balls. Unfortunately, the event devices can't
   5.479 -           currently be calibrated, so it's a win-lose situation.
   5.480 -           So : /dev/input/eventX = /dev/input/jsY = /dev/js
   5.481 -         */
   5.482 -        if ((i == 0) && (numjoysticks > 0))
   5.483 -            break;
   5.484 -    }
   5.485 -
   5.486 -    SDL_SYS_numjoysticks = numjoysticks;
   5.487 -    return (numjoysticks);
   5.488 +    return JoystickInitWithoutUdev();
   5.489  }
   5.490  
   5.491  int SDL_SYS_NumJoysticks()
   5.492  {
   5.493 -    return SDL_SYS_numjoysticks;
   5.494 +    return numjoysticks;
   5.495 +}
   5.496 +
   5.497 +static SDL_bool
   5.498 +HotplugUpdateAvailable(void)
   5.499 +{
   5.500 +#if SDL_USE_LIBUDEV
   5.501 +    if (udev_mon != NULL) {
   5.502 +        const int fd = UDEV_udev_monitor_get_fd(udev_mon);
   5.503 +        fd_set fds;
   5.504 +        struct timeval tv;
   5.505 +
   5.506 +        FD_ZERO(&fds);
   5.507 +        FD_SET(fd, &fds);
   5.508 +        tv.tv_sec = 0;
   5.509 +        tv.tv_usec = 0;
   5.510 +        if ((select(fd+1, &fds, NULL, NULL, &tv) > 0) && (FD_ISSET(fd, &fds))) {
   5.511 +            return SDL_TRUE;
   5.512 +        }
   5.513 +    }
   5.514 +#endif
   5.515 +
   5.516 +    return SDL_FALSE;
   5.517  }
   5.518  
   5.519  void SDL_SYS_JoystickDetect()
   5.520  {
   5.521 +#if SDL_USE_LIBUDEV
   5.522 +    struct udev_device *dev = NULL;
   5.523 +    const char *devnode = NULL;
   5.524 +    const char *action = NULL;
   5.525 +    const char *val = NULL;
   5.526 +
   5.527 +    while (HotplugUpdateAvailable()) {
   5.528 +        dev = UDEV_udev_monitor_receive_device(udev_mon);
   5.529 +        if (dev == NULL) {
   5.530 +            break;
   5.531 +        }
   5.532 +        val = UDEV_udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK");
   5.533 +        if ((!val) || (SDL_strcmp(val, "1") != 0)) {
   5.534 +            continue;
   5.535 +        }
   5.536 +
   5.537 +        action = UDEV_udev_device_get_action(dev);
   5.538 +        devnode = UDEV_udev_device_get_devnode(dev);
   5.539 +
   5.540 +        if (SDL_strcmp(action, "add") == 0) {
   5.541 +            const int device_index = MaybeAddDevice(devnode);
   5.542 +            if (device_index != -1) {
   5.543 +                /* !!! FIXME: Move this to an SDL_PrivateJoyDeviceAdded() function? */
   5.544 +                #if !SDL_EVENTS_DISABLED
   5.545 +                SDL_Event event;
   5.546 +                event.type = SDL_JOYDEVICEADDED;
   5.547 +
   5.548 +                if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   5.549 +                    event.jdevice.which = device_index;
   5.550 +                    if ( (SDL_EventOK == NULL) ||
   5.551 +                         (*SDL_EventOK) (SDL_EventOKParam, &event) ) {
   5.552 +                        SDL_PushEvent(&event);
   5.553 +                    }
   5.554 +                }
   5.555 +                #endif // !SDL_EVENTS_DISABLED
   5.556 +            }
   5.557 +        } else if (SDL_strcmp(action, "remove") == 0) {
   5.558 +            const int inst = MaybeRemoveDevice(devnode);
   5.559 +            if (inst != -1) {
   5.560 +                /* !!! FIXME: Move this to an SDL_PrivateJoyDeviceRemoved() function? */
   5.561 +                #if !SDL_EVENTS_DISABLED
   5.562 +                SDL_Event event;
   5.563 +                event.type = SDL_JOYDEVICEREMOVED;
   5.564 +
   5.565 +                if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   5.566 +                    event.jdevice.which = inst;
   5.567 +                    if ( (SDL_EventOK == NULL) ||
   5.568 +                         (*SDL_EventOK) (SDL_EventOKParam, &event) ) {
   5.569 +                        SDL_PushEvent(&event);
   5.570 +                    }
   5.571 +                }
   5.572 +                #endif // !SDL_EVENTS_DISABLED 
   5.573 +            }
   5.574 +        }
   5.575 +        UDEV_udev_device_unref(dev);
   5.576 +    }
   5.577 +#endif
   5.578  }
   5.579  
   5.580  SDL_bool SDL_SYS_JoystickNeedsPolling()
   5.581  {
   5.582 -    return SDL_FALSE;
   5.583 +    /*
   5.584 +     * This results in a select() call, so technically we're polling to
   5.585 +     *  decide if we should poll, but I think this function is here because
   5.586 +     *  Windows has to do an enormous amount of work to detect new sticks,
   5.587 +     *  whereas libudev just needs to see if there's more data available on
   5.588 +     *  a socket...so this should be acceptable, I hope.
   5.589 +     */
   5.590 +    return HotplugUpdateAvailable();
   5.591 +}
   5.592 +
   5.593 +static SDL_joylist_item *
   5.594 +JoystickByDevIndex(int device_index)
   5.595 +{
   5.596 +    SDL_joylist_item *item = SDL_joylist;
   5.597 +
   5.598 +    if ((device_index < 0) || (device_index >= numjoysticks)) {
   5.599 +        return NULL;
   5.600 +    }
   5.601 +
   5.602 +    while (device_index > 0) {
   5.603 +        SDL_assert(item != NULL);
   5.604 +        device_index--;
   5.605 +        item = item->next;
   5.606 +    }
   5.607 +
   5.608 +    return item;
   5.609  }
   5.610  
   5.611  /* Function to get the device-dependent name of a joystick */
   5.612  const char *
   5.613  SDL_SYS_JoystickNameForDeviceIndex(int device_index)
   5.614  {
   5.615 -    int fd;
   5.616 -    static char namebuf[128];
   5.617 -    const char *name;
   5.618 -
   5.619 -    name = NULL;
   5.620 -    fd = open(SDL_joylist[device_index].fname, O_RDONLY, 0);
   5.621 -    if (fd >= 0) {
   5.622 -        if (ioctl(fd, EVIOCGNAME(sizeof(namebuf)), namebuf) <= 0) {
   5.623 -            name = SDL_joylist[device_index].fname;
   5.624 -        } else {
   5.625 -            name = namebuf;
   5.626 -        }
   5.627 -        close(fd);
   5.628 -    }
   5.629 -    return name;
   5.630 +    return JoystickByDevIndex(device_index)->name;
   5.631  }
   5.632  
   5.633  /* Function to perform the mapping from device index to the instance id for this index */
   5.634  SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
   5.635  {
   5.636 -    return device_index;
   5.637 +    return JoystickByDevIndex(device_index)->device_instance;
   5.638  }
   5.639  
   5.640  static int
   5.641 @@ -362,28 +704,46 @@
   5.642  int
   5.643  SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
   5.644  {
   5.645 -    int fd;
   5.646 -    char *fname;
   5.647 +    SDL_joylist_item *item = JoystickByDevIndex(device_index);
   5.648 +    char *fname = NULL;
   5.649 +    int fd = -1;
   5.650  
   5.651 -    /* Open the joystick and set the joystick file descriptor */
   5.652 -    fd = open(SDL_joylist[joystick->instance_id].fname, O_RDONLY, 0);
   5.653 -    fname = SDL_joylist[joystick->instance_id].fname;
   5.654 +    if (item == NULL) {
   5.655 +        SDL_SetError("No such device");
   5.656 +        return -1;
   5.657 +    }
   5.658  
   5.659 +    fname = item->path;
   5.660 +    fd = open(fname, O_RDONLY, 0);
   5.661      if (fd < 0) {
   5.662 -        SDL_SetError("Unable to open %s\n", SDL_joylist[joystick->instance_id]);
   5.663 -        return (-1);
   5.664 +        SDL_SetError("Unable to open %s", fname);
   5.665 +        return -1;
   5.666      }
   5.667 +
   5.668      joystick->instance_id = device_index;
   5.669      joystick->hwdata = (struct joystick_hwdata *)
   5.670          SDL_malloc(sizeof(*joystick->hwdata));
   5.671      if (joystick->hwdata == NULL) {
   5.672 +        close(fd);
   5.673          SDL_OutOfMemory();
   5.674 -        close(fd);
   5.675          return (-1);
   5.676      }
   5.677      SDL_memset(joystick->hwdata, 0, sizeof(*joystick->hwdata));
   5.678 +    joystick->hwdata->removed = SDL_FALSE;
   5.679 +    joystick->hwdata->device_instance = item->device_instance;
   5.680 +    joystick->hwdata->guid = item->guid;
   5.681      joystick->hwdata->fd = fd;
   5.682 -    joystick->hwdata->fname = fname;
   5.683 +    joystick->hwdata->fname = SDL_strdup(item->path);
   5.684 +    if (joystick->hwdata->fname == NULL) {
   5.685 +        SDL_free(joystick->hwdata);
   5.686 +        joystick->hwdata = NULL;
   5.687 +        close(fd);
   5.688 +        SDL_OutOfMemory();
   5.689 +        return (-1);
   5.690 +    }
   5.691 +
   5.692 +    SDL_assert(item->hwdata == NULL);
   5.693 +    item->hwdata = joystick->hwdata;
   5.694  
   5.695      /* Set the joystick to non-blocking read mode */
   5.696      fcntl(fd, F_SETFL, O_NONBLOCK);
   5.697 @@ -397,7 +757,7 @@
   5.698  /* Function to determine is this joystick is attached to the system right now */
   5.699  SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
   5.700  {
   5.701 -    return SDL_TRUE;
   5.702 +    return !joystick->closed && !joystick->hwdata->removed;
   5.703  }
   5.704  
   5.705  static __inline__ void
   5.706 @@ -552,12 +912,9 @@
   5.707  {
   5.708      if (joystick->hwdata) {
   5.709          close(joystick->hwdata->fd);
   5.710 -        if (joystick->hwdata->hats) {
   5.711 -            SDL_free(joystick->hwdata->hats);
   5.712 -        }
   5.713 -        if (joystick->hwdata->balls) {
   5.714 -            SDL_free(joystick->hwdata->balls);
   5.715 -        }
   5.716 +        SDL_free(joystick->hwdata->hats);
   5.717 +        SDL_free(joystick->hwdata->balls);
   5.718 +        SDL_free(joystick->hwdata->fname);
   5.719          SDL_free(joystick->hwdata);
   5.720          joystick->hwdata = NULL;
   5.721      }
   5.722 @@ -568,34 +925,42 @@
   5.723  void
   5.724  SDL_SYS_JoystickQuit(void)
   5.725  {
   5.726 -    int i;
   5.727 +    SDL_joylist_item *item = NULL;
   5.728 +    SDL_joylist_item *next = NULL;
   5.729  
   5.730 -    for (i = 0; SDL_joylist[i].fname; ++i) {
   5.731 -        if (SDL_joylist[i].fname) {
   5.732 -            SDL_free(SDL_joylist[i].fname);
   5.733 -            SDL_joylist[i].fname = NULL;
   5.734 -        }
   5.735 +    for (item = SDL_joylist; item; item = next) {
   5.736 +        next = item->next;
   5.737 +        SDL_free(item->path);
   5.738 +        SDL_free(item->name);
   5.739 +        SDL_free(item);
   5.740      }
   5.741 +
   5.742 +    SDL_joylist = SDL_joylist_tail = NULL;
   5.743 +
   5.744 +    numjoysticks = 0;
   5.745 +    instance_counter = 0;
   5.746 +
   5.747 +#if SDL_USE_LIBUDEV
   5.748 +    if (udev_mon != NULL) {
   5.749 +        UDEV_udev_monitor_unref(udev_mon);
   5.750 +        udev_mon = NULL;
   5.751 +    }
   5.752 +    if (udev != NULL) {
   5.753 +        UDEV_udev_unref(udev);
   5.754 +        udev = NULL;
   5.755 +    }
   5.756 +    UnloadUDEVLibrary();
   5.757 +#endif
   5.758  }
   5.759  
   5.760  JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
   5.761  {
   5.762 -    JoystickGUID guid;
   5.763 -    // the GUID is just the first 16 chars of the name for now
   5.764 -    const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index );
   5.765 -    SDL_zero( guid );
   5.766 -    SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
   5.767 -    return guid;
   5.768 +    return JoystickByDevIndex(device_index)->guid;
   5.769  }
   5.770  
   5.771  JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
   5.772  {
   5.773 -    JoystickGUID guid;
   5.774 -    // the GUID is just the first 16 chars of the name for now
   5.775 -    const char *name = joystick->name;
   5.776 -    SDL_zero( guid );
   5.777 -    SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
   5.778 -    return guid;
   5.779 +    return joystick->hwdata->guid;
   5.780  }
   5.781  
   5.782  #endif /* SDL_JOYSTICK_LINUX */
     6.1 --- a/src/joystick/linux/SDL_sysjoystick_c.h	Tue Dec 11 11:59:29 2012 -0500
     6.2 +++ b/src/joystick/linux/SDL_sysjoystick_c.h	Tue Dec 11 12:07:06 2012 -0500
     6.3 @@ -19,14 +19,16 @@
     6.4    3. This notice may not be removed or altered from any source distribution.
     6.5  */
     6.6  
     6.7 -#if SDL_INPUT_LINUXEV
     6.8  #include <linux/input.h>
     6.9 -#endif
    6.10  
    6.11  /* The private structure used to keep track of a joystick */
    6.12  struct joystick_hwdata
    6.13  {
    6.14      int fd;
    6.15 +    int device_instance;
    6.16 +    SDL_bool removed;
    6.17 +
    6.18 +    JoystickGUID guid;
    6.19      char *fname;                /* Used in haptic subsystem */
    6.20  
    6.21      /* The current linux joystick driver maps hats to two axes */
    6.22 @@ -41,8 +43,6 @@
    6.23      } *balls;
    6.24  
    6.25      /* Support for the Linux 2.4 unified input interface */
    6.26 -#if SDL_INPUT_LINUXEV
    6.27 -    SDL_bool is_hid;
    6.28      Uint8 key_map[KEY_MAX - BTN_MISC];
    6.29      Uint8 abs_map[ABS_MAX];
    6.30      struct axis_correct
    6.31 @@ -50,5 +50,4 @@
    6.32          int used;
    6.33          int coef[3];
    6.34      } abs_correct[ABS_MAX];
    6.35 -#endif
    6.36  };