diff --git a/CMakeLists.txt b/CMakeLists.txt index 80389d100..0b561c91c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -648,7 +648,9 @@ if(UNIX AND NOT APPLE) set(SOURCE_FILES ${SOURCE_FILES} ${HAPTIC_SOURCES}) set(HAVE_SDL_HAPTIC TRUE) endif(SDL_HAPTIC AND HAVE_INPUT_EVENTS) - endif(LINUX + + check_include_file("libudev.h" HAVE_LIBUDEV_H) + endif(LINUX) if(INPUT_TSLIB) check_c_source_compiles(" diff --git a/configure.in b/configure.in index b7f974df5..8fd9280c4 100644 --- a/configure.in +++ b/configure.in @@ -1667,6 +1667,22 @@ CheckInputEvents() fi } +dnl See if the platform offers libudev for device enumeration and hotplugging. +CheckLibUDev() +{ + AC_ARG_ENABLE(libudev, +AC_HELP_STRING([--enable-libudev], [enable libudev support [[default=yes]]]), + , enable_libudev=yes) + if test x$enable_libudev = xyes; then + AC_CHECK_HEADER(libudev.h, + have_libudev_h_hdr=yes, + have_libudev_h_hdr=no) + if test x$have_libudev_h_hdr = xyes; then + AC_DEFINE(HAVE_LIBUDEV_H, 1, [ ]) + fi + fi +} + dnl See if we can use the Touchscreen input library CheckTslib() { @@ -2191,6 +2207,7 @@ case "$host" in CheckFusionSound CheckOpenGLX11 CheckOpenGLESX11 + CheckLibUDev CheckInputEvents CheckTslib CheckUSBHID diff --git a/include/SDL_config.h.cmake b/include/SDL_config.h.cmake index 6328eb9f5..d5ae1d815 100644 --- a/include/SDL_config.h.cmake +++ b/include/SDL_config.h.cmake @@ -65,6 +65,7 @@ #cmakedefine HAVE_SIGNAL_H 1 #cmakedefine HAVE_ALTIVEC_H 1 #cmakedefine HAVE_PTHREAD_NP_H 1 +#cmakedefine HAVE_LIBUDEV_H 1 /* C library functions */ #cmakedefine HAVE_MALLOC 1 diff --git a/include/SDL_config.h.in b/include/SDL_config.h.in index 0e271a83c..bcf443d48 100644 --- a/include/SDL_config.h.in +++ b/include/SDL_config.h.in @@ -70,6 +70,7 @@ #undef HAVE_SIGNAL_H #undef HAVE_ALTIVEC_H #undef HAVE_PTHREAD_NP_H +#undef HAVE_LIBUDEV_H /* C library functions */ #undef HAVE_MALLOC diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c index 938d1d6d7..77d75800f 100644 --- a/src/joystick/linux/SDL_sysjoystick.c +++ b/src/joystick/linux/SDL_sysjoystick.c @@ -35,31 +35,168 @@ #include /* For the definition of PATH_MAX */ #include +#include "SDL_assert.h" #include "SDL_joystick.h" +#include "SDL_endian.h" #include "../SDL_sysjoystick.h" #include "../SDL_joystick_c.h" #include "SDL_sysjoystick_c.h" -/* The maximum number of joysticks we'll detect */ -#define MAX_JOYSTICKS 32 +/* !!! FIXME: move this somewhere else. */ +#if !SDL_EVENTS_DISABLED +#include "../../events/SDL_events_c.h" +#endif -/* A list of available joysticks */ -static struct +/* + * !!! FIXME: move all the udev stuff to src/core/linux, so I can reuse it + * !!! FIXME: for audio hardware disconnects. + */ +#ifdef HAVE_LIBUDEV_H +#define SDL_USE_LIBUDEV 1 +#include "SDL_loadso.h" +#include +#include +#include +#include + +/* we never link directly to libudev. */ +/* !!! FIXME: can we generalize this? ALSA, etc, do the same things. */ +static const char *udev_library = "libudev.so"; +static void *udev_handle = NULL; + +/* !!! FIXME: this is kinda ugly. */ +static SDL_bool +load_udev_sym(const char *fn, void **addr) { - char *fname; -} SDL_joylist[MAX_JOYSTICKS]; + *addr = SDL_LoadFunction(udev_handle, fn); + if (*addr == NULL) { + /* Don't call SDL_SetError(): SDL_LoadFunction already did. */ + return SDL_FALSE; + } + return SDL_TRUE; +} + +/* libudev entry points... */ +static const char *(*UDEV_udev_device_get_action)(struct udev_device *) = NULL; +static const char *(*UDEV_udev_device_get_devnode)(struct udev_device *) = NULL; +static const char *(*UDEV_udev_device_get_property_value)(struct udev_device *, const char *) = NULL; +static struct udev_device *(*UDEV_udev_device_new_from_syspath)(struct udev *, const char *) = NULL; +static void (*UDEV_udev_device_unref)(struct udev_device *) = NULL; +static int (*UDEV_udev_enumerate_add_match_property)(struct udev_enumerate *, const char *, const char *) = NULL; +static int (*UDEV_udev_enumerate_add_match_subsystem)(struct udev_enumerate *, const char *) = NULL; +static struct udev_list_entry *(*UDEV_udev_enumerate_get_list_entry)(struct udev_enumerate *) = NULL; +static struct udev_enumerate *(*UDEV_udev_enumerate_new)(struct udev *) = NULL; +static int (*UDEV_udev_enumerate_scan_devices)(struct udev_enumerate *) = NULL; +static void (*UDEV_udev_enumerate_unref)(struct udev_enumerate *) = NULL; +static const char *(*UDEV_udev_list_entry_get_name)(struct udev_list_entry *) = NULL; +static struct udev_list_entry *(*UDEV_udev_list_entry_get_next)(struct udev_list_entry *) = NULL; +static int (*UDEV_udev_monitor_enable_receiving)(struct udev_monitor *) = NULL; +static int (*UDEV_udev_monitor_filter_add_match_subsystem_devtype)(struct udev_monitor *, const char *, const char *) = NULL; +static int (*UDEV_udev_monitor_get_fd)(struct udev_monitor *) = NULL; +static struct udev_monitor *(*UDEV_udev_monitor_new_from_netlink)(struct udev *, const char *) = NULL; +static struct udev_device *(*UDEV_udev_monitor_receive_device)(struct udev_monitor *) = NULL; +static void (*UDEV_udev_monitor_unref)(struct udev_monitor *) = NULL; +static struct udev *(*UDEV_udev_new)(void) = NULL; +static void (*UDEV_udev_unref)(struct udev *) = NULL; + +static int +load_udev_syms(void) +{ + /* cast funcs to char* first, to please GCC's strict aliasing rules. */ + #define SDL_UDEV_SYM(x) \ + if (!load_udev_sym(#x, (void **) (char *) &UDEV_##x)) return -1 + + SDL_UDEV_SYM(udev_device_get_action); + SDL_UDEV_SYM(udev_device_get_devnode); + SDL_UDEV_SYM(udev_device_get_property_value); + SDL_UDEV_SYM(udev_device_new_from_syspath); + SDL_UDEV_SYM(udev_device_unref); + SDL_UDEV_SYM(udev_enumerate_add_match_property); + SDL_UDEV_SYM(udev_enumerate_add_match_subsystem); + SDL_UDEV_SYM(udev_enumerate_get_list_entry); + SDL_UDEV_SYM(udev_enumerate_new); + SDL_UDEV_SYM(udev_enumerate_scan_devices); + SDL_UDEV_SYM(udev_enumerate_unref); + SDL_UDEV_SYM(udev_list_entry_get_name); + SDL_UDEV_SYM(udev_list_entry_get_next); + SDL_UDEV_SYM(udev_monitor_enable_receiving); + SDL_UDEV_SYM(udev_monitor_filter_add_match_subsystem_devtype); + SDL_UDEV_SYM(udev_monitor_get_fd); + SDL_UDEV_SYM(udev_monitor_new_from_netlink); + SDL_UDEV_SYM(udev_monitor_receive_device); + SDL_UDEV_SYM(udev_monitor_unref); + SDL_UDEV_SYM(udev_new); + SDL_UDEV_SYM(udev_unref); + + #undef SDL_UDEV_SYM + + return 0; +} + +static void +UnloadUDEVLibrary(void) +{ + if (udev_handle != NULL) { + SDL_UnloadObject(udev_handle); + udev_handle = NULL; + } +} + +static int +LoadUDEVLibrary(void) +{ + int retval = 0; + if (udev_handle == NULL) { + udev_handle = SDL_LoadObject(udev_library); + if (udev_handle == NULL) { + retval = -1; + /* Don't call SDL_SetError(): SDL_LoadObject already did. */ + } else { + retval = load_udev_syms(); + if (retval < 0) { + UnloadUDEVLibrary(); + } + } + } + + return retval; +} + +static struct udev *udev = NULL; +static struct udev_monitor *udev_mon = NULL; +#endif + + +/* A linked list of available joysticks */ +typedef struct SDL_joylist_item +{ + int device_instance; + char *path; /* "/dev/input/event2" or whatever */ + char *name; /* "SideWinder 3D Pro" or whatever */ + JoystickGUID guid; + dev_t devnum; + struct joystick_hwdata *hwdata; + struct SDL_joylist_item *next; +} SDL_joylist_item; + +static SDL_joylist_item *SDL_joylist = NULL; +static SDL_joylist_item *SDL_joylist_tail = NULL; +static int numjoysticks = 0; +static int instance_counter = 0; #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) #define NBITS(x) ((((x)-1)/(sizeof(long) * 8))+1) static int -IsJoystick(int fd) +IsJoystick(int fd, char *namebuf, const size_t namebuflen, JoystickGUID *guid) { unsigned long evbit[NBITS(EV_MAX)] = { 0 }; unsigned long keybit[NBITS(KEY_MAX)] = { 0 }; unsigned long absbit[NBITS(ABS_MAX)] = { 0 }; + struct input_id inpid; + Uint16 *guid16 = (Uint16 *) ((char *) &guid->data); if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) || (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) || @@ -71,30 +208,206 @@ IsJoystick(int fd) test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit))) { return 0; } - return (1); + + if (ioctl(fd, EVIOCGNAME(namebuflen), namebuf) < 0) { + return 0; + } + + if (ioctl(fd, EVIOCGID, &inpid) < 0) { + return 0; + } + + /* We only need 16 bits for each of these; space them out to fill 128. */ + /* Byteswap so devices get same GUID on little/big endian platforms. */ + *(guid16++) = SDL_SwapLE16(inpid.bustype); + *(guid16++) = 0; + *(guid16++) = SDL_SwapLE16(inpid.vendor); + *(guid16++) = 0; + *(guid16++) = SDL_SwapLE16(inpid.product); + *(guid16++) = 0; + *(guid16++) = SDL_SwapLE16(inpid.version); + *(guid16++) = 0; + + return 1; } -static int SDL_SYS_numjoysticks = 0; +/* !!! FIXME: I would love to dump this code and use libudev instead. */ +static int +MaybeAddDevice(const char *path) +{ + struct stat sb; + int fd = -1; + int isstick = 0; + char namebuf[128]; + JoystickGUID guid; + SDL_joylist_item *item; + + if (path == NULL) { + return -1; + } + + if (stat(path, &sb) == -1) { + return -1; + } + + /* Check to make sure it's not already in list. */ + for (item = SDL_joylist; item != NULL; item = item->next) { + if (sb.st_rdev == item->devnum) { + return -1; /* already have this one */ + } + } + + fd = open(path, O_RDONLY, 0); + if (fd < 0) { + return -1; + } + +#ifdef DEBUG_INPUT_EVENTS + printf("Checking %s\n", path); +#endif + + isstick = IsJoystick(fd, namebuf, sizeof (namebuf), &guid); + close(fd); + if (!isstick) { + return -1; + } + + item = (SDL_joylist_item *) SDL_malloc(sizeof (SDL_joylist_item)); + if (item == NULL) { + return -1; + } + + SDL_zerop(item); + item->devnum = sb.st_rdev; + item->path = SDL_strdup(path); + item->name = SDL_strdup(namebuf); + item->guid = guid; + + if ( (item->path == NULL) || (item->name == NULL) ) { + SDL_free(item->path); + SDL_free(item->name); + SDL_free(item); + return -1; + } + + item->device_instance = instance_counter++; + if (SDL_joylist_tail == NULL) { + SDL_joylist = SDL_joylist_tail = item; + } else { + SDL_joylist_tail->next = item; + SDL_joylist_tail = item; + } + + return numjoysticks++; +} + +/* !!! FIXME: I would love to dump this code and use libudev instead. */ +static int +MaybeRemoveDevice(const char *path) +{ + SDL_joylist_item *item; + SDL_joylist_item *prev = NULL; + + if (path == NULL) { + return -1; + } + + for (item = SDL_joylist; item != NULL; item = item->next) { + /* found it, remove it. */ + if (SDL_strcmp(path, item->path) == 0) { + const int retval = item->device_instance; + if (item->hwdata) { + item->hwdata->removed = SDL_TRUE; + } + if (prev != NULL) { + prev->next = item->next; + if (item == SDL_joylist_tail) { + SDL_joylist_tail = prev; + } + } else { + SDL_assert(!SDL_joylist); + SDL_assert(!SDL_joylist_tail); + SDL_joylist = SDL_joylist_tail = NULL; + } + SDL_free(item->path); + SDL_free(item->name); + SDL_free(item); + numjoysticks--; + return retval; + } + prev = item; + } + + return -1; +} + +static int +JoystickInitWithoutUdev(void) +{ + int i; + char path[PATH_MAX]; + + /* !!! 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); + } + + return numjoysticks; +} + + +#if SDL_USE_LIBUDEV +static int +JoystickInitWithUdev(void) +{ + struct udev_enumerate *enumerate = NULL; + struct udev_list_entry *devs = NULL; + struct udev_list_entry *item = NULL; + + SDL_assert(udev == NULL); + udev = UDEV_udev_new(); + if (udev == NULL) { + SDL_SetError("udev_new() failed"); + return -1; + } + + udev_mon = UDEV_udev_monitor_new_from_netlink(udev, "udev"); + if (udev_mon != NULL) { /* okay if it's NULL, we just lose hotplugging. */ + UDEV_udev_monitor_filter_add_match_subsystem_devtype(udev_mon, + "input", NULL); + UDEV_udev_monitor_enable_receiving(udev_mon); + } + + enumerate = UDEV_udev_enumerate_new(udev); + if (enumerate == NULL) { + SDL_SetError("udev_enumerate_new() failed"); + return -1; + } + + UDEV_udev_enumerate_add_match_subsystem(enumerate, "input"); + UDEV_udev_enumerate_add_match_property(enumerate, "ID_INPUT_JOYSTICK", "1"); + UDEV_udev_enumerate_scan_devices(enumerate); + devs = UDEV_udev_enumerate_get_list_entry(enumerate); + for (item = devs; item; item = UDEV_udev_list_entry_get_next(item)) { + const char *path = UDEV_udev_list_entry_get_name(item); + struct udev_device *dev = UDEV_udev_device_new_from_syspath(udev, path); + MaybeAddDevice(UDEV_udev_device_get_devnode(dev)); + UDEV_udev_device_unref(dev); + } + + UDEV_udev_enumerate_unref(enumerate); + + return numjoysticks; +} +#endif -/* Function to scan the system for joysticks */ int SDL_SYS_JoystickInit(void) { - /* The base path of the joystick devices */ - const char *joydev_pattern[] = { - "/dev/input/event%d", - }; - int numjoysticks; - int i, j; - int fd; - char path[PATH_MAX]; - dev_t dev_nums[MAX_JOYSTICKS]; /* major/minor device numbers */ - struct stat sb; - int n, duplicate; - - numjoysticks = 0; - /* First see if the user specified one or more joysticks to use */ if (SDL_getenv("SDL_JOYSTICK_DEVICE") != NULL) { char *envcopy, *envpath, *delim; @@ -105,121 +418,150 @@ SDL_SYS_JoystickInit(void) if (delim != NULL) { *delim++ = '\0'; } - if (stat(envpath, &sb) == 0) { - fd = open(envpath, O_RDONLY, 0); - if (fd >= 0) { - /* Assume the user knows what they're doing. */ - SDL_joylist[numjoysticks].fname = SDL_strdup(envpath); - if (SDL_joylist[numjoysticks].fname) { - dev_nums[numjoysticks] = sb.st_rdev; - ++numjoysticks; - } - close(fd); - } - } + MaybeAddDevice(envpath); envpath = delim; } SDL_free(envcopy); } - for (i = 0; i < SDL_arraysize(joydev_pattern); ++i) { - for (j = 0; j < MAX_JOYSTICKS; ++j) { - SDL_snprintf(path, SDL_arraysize(path), joydev_pattern[i], j); - - /* rcg06302000 replaced access(F_OK) call with stat(). - * stat() will fail if the file doesn't exist, so it's - * equivalent behaviour. - */ - if (stat(path, &sb) == 0) { - /* Check to make sure it's not already in list. - * This happens when we see a stick via symlink. - */ - duplicate = 0; - for (n = 0; (n < numjoysticks) && !duplicate; ++n) { - if (sb.st_rdev == dev_nums[n]) { - duplicate = 1; - } - } - if (duplicate) { - continue; - } - - fd = open(path, O_RDONLY, 0); - if (fd < 0) { - continue; - } -#ifdef DEBUG_INPUT_EVENTS - printf("Checking %s\n", path); -#endif - if ((i == 0) && !IsJoystick(fd)) { - close(fd); - continue; - } - close(fd); - - /* We're fine, add this joystick */ - SDL_joylist[numjoysticks].fname = SDL_strdup(path); - if (SDL_joylist[numjoysticks].fname) { - dev_nums[numjoysticks] = sb.st_rdev; - ++numjoysticks; - } - } - } - - /* This is a special case... - If the event devices are valid then the joystick devices - will be duplicates but without extra information about their - hats or balls. Unfortunately, the event devices can't - currently be calibrated, so it's a win-lose situation. - So : /dev/input/eventX = /dev/input/jsY = /dev/js - */ - if ((i == 0) && (numjoysticks > 0)) - break; +#if SDL_USE_LIBUDEV + if (LoadUDEVLibrary() == 0) { /* okay if this fails, FOR NOW. */ + return JoystickInitWithUdev(); } +#endif - SDL_SYS_numjoysticks = numjoysticks; - return (numjoysticks); + return JoystickInitWithoutUdev(); } int SDL_SYS_NumJoysticks() { - return SDL_SYS_numjoysticks; + return numjoysticks; +} + +static SDL_bool +HotplugUpdateAvailable(void) +{ +#if SDL_USE_LIBUDEV + if (udev_mon != NULL) { + const int fd = UDEV_udev_monitor_get_fd(udev_mon); + fd_set fds; + struct timeval tv; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + tv.tv_sec = 0; + tv.tv_usec = 0; + if ((select(fd+1, &fds, NULL, NULL, &tv) > 0) && (FD_ISSET(fd, &fds))) { + return SDL_TRUE; + } + } +#endif + + return SDL_FALSE; } void SDL_SYS_JoystickDetect() { +#if SDL_USE_LIBUDEV + struct udev_device *dev = NULL; + const char *devnode = NULL; + const char *action = NULL; + const char *val = NULL; + + while (HotplugUpdateAvailable()) { + dev = UDEV_udev_monitor_receive_device(udev_mon); + if (dev == NULL) { + break; + } + val = UDEV_udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK"); + if ((!val) || (SDL_strcmp(val, "1") != 0)) { + continue; + } + + action = UDEV_udev_device_get_action(dev); + devnode = UDEV_udev_device_get_devnode(dev); + + if (SDL_strcmp(action, "add") == 0) { + const int device_index = MaybeAddDevice(devnode); + if (device_index != -1) { + /* !!! FIXME: Move this to an SDL_PrivateJoyDeviceAdded() function? */ + #if !SDL_EVENTS_DISABLED + SDL_Event event; + event.type = SDL_JOYDEVICEADDED; + + if (SDL_GetEventState(event.type) == SDL_ENABLE) { + event.jdevice.which = device_index; + if ( (SDL_EventOK == NULL) || + (*SDL_EventOK) (SDL_EventOKParam, &event) ) { + SDL_PushEvent(&event); + } + } + #endif // !SDL_EVENTS_DISABLED + } + } else if (SDL_strcmp(action, "remove") == 0) { + const int inst = MaybeRemoveDevice(devnode); + if (inst != -1) { + /* !!! FIXME: Move this to an SDL_PrivateJoyDeviceRemoved() function? */ + #if !SDL_EVENTS_DISABLED + SDL_Event event; + event.type = SDL_JOYDEVICEREMOVED; + + if (SDL_GetEventState(event.type) == SDL_ENABLE) { + event.jdevice.which = inst; + if ( (SDL_EventOK == NULL) || + (*SDL_EventOK) (SDL_EventOKParam, &event) ) { + SDL_PushEvent(&event); + } + } + #endif // !SDL_EVENTS_DISABLED + } + } + UDEV_udev_device_unref(dev); + } +#endif } SDL_bool SDL_SYS_JoystickNeedsPolling() { - return SDL_FALSE; + /* + * This results in a select() call, so technically we're polling to + * decide if we should poll, but I think this function is here because + * Windows has to do an enormous amount of work to detect new sticks, + * whereas libudev just needs to see if there's more data available on + * a socket...so this should be acceptable, I hope. + */ + return HotplugUpdateAvailable(); +} + +static SDL_joylist_item * +JoystickByDevIndex(int device_index) +{ + SDL_joylist_item *item = SDL_joylist; + + if ((device_index < 0) || (device_index >= numjoysticks)) { + return NULL; + } + + while (device_index > 0) { + SDL_assert(item != NULL); + device_index--; + item = item->next; + } + + return item; } /* Function to get the device-dependent name of a joystick */ const char * SDL_SYS_JoystickNameForDeviceIndex(int device_index) { - int fd; - static char namebuf[128]; - const char *name; - - name = NULL; - fd = open(SDL_joylist[device_index].fname, O_RDONLY, 0); - if (fd >= 0) { - if (ioctl(fd, EVIOCGNAME(sizeof(namebuf)), namebuf) <= 0) { - name = SDL_joylist[device_index].fname; - } else { - name = namebuf; - } - close(fd); - } - return name; + return JoystickByDevIndex(device_index)->name; } /* Function to perform the mapping from device index to the instance id for this index */ SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index) { - return device_index; + return JoystickByDevIndex(device_index)->device_instance; } static int @@ -362,28 +704,46 @@ ConfigJoystick(SDL_Joystick * joystick, int fd) int SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index) { - int fd; - char *fname; + SDL_joylist_item *item = JoystickByDevIndex(device_index); + char *fname = NULL; + int fd = -1; - /* Open the joystick and set the joystick file descriptor */ - fd = open(SDL_joylist[joystick->instance_id].fname, O_RDONLY, 0); - fname = SDL_joylist[joystick->instance_id].fname; - - if (fd < 0) { - SDL_SetError("Unable to open %s\n", SDL_joylist[joystick->instance_id]); - return (-1); + if (item == NULL) { + SDL_SetError("No such device"); + return -1; } + + fname = item->path; + fd = open(fname, O_RDONLY, 0); + if (fd < 0) { + SDL_SetError("Unable to open %s", fname); + return -1; + } + joystick->instance_id = device_index; joystick->hwdata = (struct joystick_hwdata *) SDL_malloc(sizeof(*joystick->hwdata)); if (joystick->hwdata == NULL) { - SDL_OutOfMemory(); close(fd); + SDL_OutOfMemory(); return (-1); } SDL_memset(joystick->hwdata, 0, sizeof(*joystick->hwdata)); + joystick->hwdata->removed = SDL_FALSE; + joystick->hwdata->device_instance = item->device_instance; + joystick->hwdata->guid = item->guid; joystick->hwdata->fd = fd; - joystick->hwdata->fname = fname; + joystick->hwdata->fname = SDL_strdup(item->path); + if (joystick->hwdata->fname == NULL) { + SDL_free(joystick->hwdata); + joystick->hwdata = NULL; + close(fd); + SDL_OutOfMemory(); + return (-1); + } + + SDL_assert(item->hwdata == NULL); + item->hwdata = joystick->hwdata; /* Set the joystick to non-blocking read mode */ fcntl(fd, F_SETFL, O_NONBLOCK); @@ -397,7 +757,7 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index) /* Function to determine is this joystick is attached to the system right now */ SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick) { - return SDL_TRUE; + return !joystick->closed && !joystick->hwdata->removed; } static __inline__ void @@ -552,12 +912,9 @@ SDL_SYS_JoystickClose(SDL_Joystick * joystick) { if (joystick->hwdata) { close(joystick->hwdata->fd); - if (joystick->hwdata->hats) { - SDL_free(joystick->hwdata->hats); - } - if (joystick->hwdata->balls) { - SDL_free(joystick->hwdata->balls); - } + SDL_free(joystick->hwdata->hats); + SDL_free(joystick->hwdata->balls); + SDL_free(joystick->hwdata->fname); SDL_free(joystick->hwdata); joystick->hwdata = NULL; } @@ -568,34 +925,42 @@ SDL_SYS_JoystickClose(SDL_Joystick * joystick) void SDL_SYS_JoystickQuit(void) { - int i; + SDL_joylist_item *item = NULL; + SDL_joylist_item *next = NULL; - for (i = 0; SDL_joylist[i].fname; ++i) { - if (SDL_joylist[i].fname) { - SDL_free(SDL_joylist[i].fname); - SDL_joylist[i].fname = NULL; - } + for (item = SDL_joylist; item; item = next) { + next = item->next; + SDL_free(item->path); + SDL_free(item->name); + SDL_free(item); } + + SDL_joylist = SDL_joylist_tail = NULL; + + numjoysticks = 0; + instance_counter = 0; + +#if SDL_USE_LIBUDEV + if (udev_mon != NULL) { + UDEV_udev_monitor_unref(udev_mon); + udev_mon = NULL; + } + if (udev != NULL) { + UDEV_udev_unref(udev); + udev = NULL; + } + UnloadUDEVLibrary(); +#endif } JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index ) { - JoystickGUID guid; - // the GUID is just the first 16 chars of the name for now - const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index ); - SDL_zero( guid ); - SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) ); - return guid; + return JoystickByDevIndex(device_index)->guid; } JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick) { - JoystickGUID guid; - // the GUID is just the first 16 chars of the name for now - const char *name = joystick->name; - SDL_zero( guid ); - SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) ); - return guid; + return joystick->hwdata->guid; } #endif /* SDL_JOYSTICK_LINUX */ diff --git a/src/joystick/linux/SDL_sysjoystick_c.h b/src/joystick/linux/SDL_sysjoystick_c.h index a6c382f0e..88bc2f344 100644 --- a/src/joystick/linux/SDL_sysjoystick_c.h +++ b/src/joystick/linux/SDL_sysjoystick_c.h @@ -19,14 +19,16 @@ 3. This notice may not be removed or altered from any source distribution. */ -#if SDL_INPUT_LINUXEV #include -#endif /* The private structure used to keep track of a joystick */ struct joystick_hwdata { int fd; + int device_instance; + SDL_bool removed; + + JoystickGUID guid; char *fname; /* Used in haptic subsystem */ /* The current linux joystick driver maps hats to two axes */ @@ -41,8 +43,6 @@ struct joystick_hwdata } *balls; /* Support for the Linux 2.4 unified input interface */ -#if SDL_INPUT_LINUXEV - SDL_bool is_hid; Uint8 key_map[KEY_MAX - BTN_MISC]; Uint8 abs_map[ABS_MAX]; struct axis_correct @@ -50,5 +50,4 @@ struct joystick_hwdata int used; int coef[3]; } abs_correct[ABS_MAX]; -#endif };