From 5fb0be30790ff3541c45c6dc0d9645638042edd9 Mon Sep 17 00:00:00 2001 From: Edward Rudd Date: Tue, 4 Feb 2014 15:44:09 -0500 Subject: [PATCH] Rework haptic backend to properly support hotplugging of haptic devices. * currently only linux backend updated. --- src/haptic/SDL_haptic.c | 127 +++++++------- src/haptic/SDL_syshaptic.h | 4 + src/haptic/linux/SDL_syshaptic.c | 286 ++++++++++++++++++++++++------- 3 files changed, 296 insertions(+), 121 deletions(-) diff --git a/src/haptic/SDL_haptic.c b/src/haptic/SDL_haptic.c index 2bbcd18c46afa..fbfe14f3bbbd4 100644 --- a/src/haptic/SDL_haptic.c +++ b/src/haptic/SDL_haptic.c @@ -25,8 +25,7 @@ #include "../joystick/SDL_joystick_c.h" /* For SDL_PrivateJoystickValid */ #include "SDL_assert.h" -Uint8 SDL_numhaptics = 0; -SDL_Haptic **SDL_haptics = NULL; +SDL_Haptic *SDL_haptics = NULL; /* @@ -35,20 +34,10 @@ SDL_Haptic **SDL_haptics = NULL; int SDL_HapticInit(void) { - int arraylen; int status; - SDL_numhaptics = 0; status = SDL_SYS_HapticInit(); if (status >= 0) { - arraylen = (status + 1) * sizeof(*SDL_haptics); - SDL_haptics = (SDL_Haptic **) SDL_malloc(arraylen); - if (SDL_haptics == NULL) { /* Out of memory. */ - SDL_numhaptics = 0; - } else { - SDL_memset(SDL_haptics, 0, arraylen); - SDL_numhaptics = status; - } status = 0; } @@ -62,16 +51,19 @@ SDL_HapticInit(void) static int ValidHaptic(SDL_Haptic * haptic) { - int i; int valid; + SDL_Haptic *hapticlist; valid = 0; if (haptic != NULL) { - for (i = 0; i < SDL_numhaptics; i++) { - if (SDL_haptics[i] == haptic) { + hapticlist = SDL_haptics; + while ( hapticlist ) + { + if (hapticlist == haptic) { valid = 1; break; } + hapticlist = hapticlist->next; } } @@ -90,7 +82,7 @@ ValidHaptic(SDL_Haptic * haptic) int SDL_NumHaptics(void) { - return SDL_numhaptics; + return SDL_SYS_NumHaptics(); } @@ -100,9 +92,9 @@ SDL_NumHaptics(void) const char * SDL_HapticName(int device_index) { - if ((device_index < 0) || (device_index >= SDL_numhaptics)) { + if ((device_index < 0) || (device_index >= SDL_NumHaptics())) { SDL_SetError("Haptic: There are %d haptic devices available", - SDL_numhaptics); + SDL_NumHaptics()); return NULL; } return SDL_SYS_HapticName(device_index); @@ -115,22 +107,27 @@ SDL_HapticName(int device_index) SDL_Haptic * SDL_HapticOpen(int device_index) { - int i; SDL_Haptic *haptic; + SDL_Haptic *hapticlist; - if ((device_index < 0) || (device_index >= SDL_numhaptics)) { + if ((device_index < 0) || (device_index >= SDL_NumHaptics())) { SDL_SetError("Haptic: There are %d haptic devices available", - SDL_numhaptics); + SDL_NumHaptics()); return NULL; } - /* If the haptic is already open, return it */ - for (i = 0; SDL_haptics[i]; i++) { - if (device_index == SDL_haptics[i]->index) { - haptic = SDL_haptics[i]; + hapticlist = SDL_haptics; + /* If the haptic is already open, return it + * TODO: Should we create haptic instance IDs like the Joystick API? + */ + while ( hapticlist ) + { + if (device_index == hapticlist->index) { + haptic = hapticlist; ++haptic->ref_count; return haptic; } + hapticlist = hapticlist->next; } /* Create the haptic device */ @@ -150,15 +147,10 @@ SDL_HapticOpen(int device_index) } /* Add haptic to list */ - for (i = 0; SDL_haptics[i]; i++) - /* Skip to next haptic */ ; - if (i >= SDL_numhaptics) { - SDL_free(haptic); - SDL_SetError("Haptic: Trying to add device past the number originally detected"); - return NULL; - } - SDL_haptics[i] = haptic; ++haptic->ref_count; + /* Link the haptic in the list */ + haptic->next = SDL_haptics; + SDL_haptics = haptic; /* Disable autocenter and set gain to max. */ if (haptic->supported & SDL_HAPTIC_GAIN) @@ -176,21 +168,26 @@ SDL_HapticOpen(int device_index) int SDL_HapticOpened(int device_index) { - int i, opened; + int opened; + SDL_Haptic *hapticlist; /* Make sure it's valid. */ - if ((device_index < 0) || (device_index >= SDL_numhaptics)) { + if ((device_index < 0) || (device_index >= SDL_NumHaptics())) { SDL_SetError("Haptic: There are %d haptic devices available", - SDL_numhaptics); + SDL_NumHaptics()); return 0; } opened = 0; - for (i = 0; SDL_haptics[i]; i++) { - if (SDL_haptics[i]->index == (Uint8) device_index) { + hapticlist = SDL_haptics; + /* TODO Should this use an instance ID? */ + while ( hapticlist ) + { + if (hapticlist->index == (Uint8) device_index) { opened = 1; break; } + hapticlist = hapticlist->next; } return opened; } @@ -271,13 +268,13 @@ SDL_JoystickIsHaptic(SDL_Joystick * joystick) SDL_Haptic * SDL_HapticOpenFromJoystick(SDL_Joystick * joystick) { - int i; SDL_Haptic *haptic; + SDL_Haptic *hapticlist; /* Make sure there is room. */ - if (SDL_numhaptics <= 0) { + if (SDL_NumHaptics() <= 0) { SDL_SetError("Haptic: There are %d haptic devices available", - SDL_numhaptics); + SDL_NumHaptics()); return NULL; } @@ -293,13 +290,16 @@ SDL_HapticOpenFromJoystick(SDL_Joystick * joystick) return NULL; } + hapticlist = SDL_haptics; /* Check to see if joystick's haptic is already open */ - for (i = 0; SDL_haptics[i]; i++) { - if (SDL_SYS_JoystickSameHaptic(SDL_haptics[i], joystick)) { - haptic = SDL_haptics[i]; + while ( hapticlist ) + { + if (SDL_SYS_JoystickSameHaptic(hapticlist, joystick)) { + haptic = hapticlist; ++haptic->ref_count; return haptic; } + hapticlist = hapticlist->next; } /* Create the haptic device */ @@ -318,15 +318,10 @@ SDL_HapticOpenFromJoystick(SDL_Joystick * joystick) } /* Add haptic to list */ - for (i = 0; SDL_haptics[i]; i++) - /* Skip to next haptic */ ; - if (i >= SDL_numhaptics) { - SDL_free(haptic); - SDL_SetError("Haptic: Trying to add device past the number originally detected"); - return NULL; - } - SDL_haptics[i] = haptic; ++haptic->ref_count; + /* Link the haptic in the list */ + haptic->next = SDL_haptics; + SDL_haptics = haptic; return haptic; } @@ -339,6 +334,8 @@ void SDL_HapticClose(SDL_Haptic * haptic) { int i; + SDL_Haptic *hapticlist; + SDL_Haptic *hapticlistprev; /* Must be valid */ if (!ValidHaptic(haptic)) { @@ -359,13 +356,26 @@ SDL_HapticClose(SDL_Haptic * haptic) SDL_SYS_HapticClose(haptic); /* Remove from the list */ - for (i = 0; SDL_haptics[i]; ++i) { - if (haptic == SDL_haptics[i]) { - SDL_haptics[i] = NULL; - SDL_memcpy(&SDL_haptics[i], &SDL_haptics[i + 1], - (SDL_numhaptics - i) * sizeof(haptic)); + hapticlist = SDL_haptics; + hapticlistprev = NULL; + while ( hapticlist ) + { + if (haptic == hapticlist) + { + if ( hapticlistprev ) + { + /* unlink this entry */ + hapticlistprev->next = hapticlist->next; + } + else + { + SDL_haptics = haptic->next; + } + break; } + hapticlistprev = hapticlist; + hapticlist = hapticlist->next; } /* Free */ @@ -379,9 +389,8 @@ void SDL_HapticQuit(void) { SDL_SYS_HapticQuit(); - SDL_free(SDL_haptics); + SDL_assert(SDL_haptics == NULL); SDL_haptics = NULL; - SDL_numhaptics = 0; } /* diff --git a/src/haptic/SDL_syshaptic.h b/src/haptic/SDL_syshaptic.h index 5c3735daf2b0a..bc0dae89651fe 100644 --- a/src/haptic/SDL_syshaptic.h +++ b/src/haptic/SDL_syshaptic.h @@ -54,6 +54,7 @@ struct _SDL_Haptic int rumble_id; /* ID of rumble effect for simple rumble API. */ SDL_HapticEffect rumble_effect; /* Rumble effect. */ + struct _SDL_Haptic *next; /* pointer to next haptic we have allocated */ }; /* @@ -63,6 +64,9 @@ struct _SDL_Haptic */ extern int SDL_SYS_HapticInit(void); +/* Function to return the number of haptic devices plugged in right now */ +extern int SDL_SYS_NumHaptics(); + /* * Gets the device dependent name of the haptic device */ diff --git a/src/haptic/linux/SDL_syshaptic.c b/src/haptic/linux/SDL_syshaptic.c index 47860d8d45931..247cb28de1b6c 100644 --- a/src/haptic/linux/SDL_syshaptic.c +++ b/src/haptic/linux/SDL_syshaptic.c @@ -22,11 +22,13 @@ #ifdef SDL_HAPTIC_LINUX +#include "SDL_assert.h" #include "SDL_haptic.h" #include "../SDL_syshaptic.h" #include "SDL_joystick.h" #include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */ #include "../../joystick/linux/SDL_sysjoystick_c.h" /* For joystick hwdata */ +#include "../../core/linux/SDL_udev.h" #include /* close */ #include /* Force feedback linux stuff. */ @@ -44,15 +46,21 @@ #define MAX_HAPTICS 32 /* It's doubtful someone has more then 32 evdev */ +static int MaybeAddDevice(const char *path); +#if SDL_USE_LIBUDEV +static int MaybeRemoveDevice(const char *path); +void haptic_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath); +#endif /* SDL_USE_LIBUDEV */ /* * List of available haptic devices. */ -static struct +typedef struct SDL_hapticlist_item { char *fname; /* Dev path name (like /dev/input/event1) */ SDL_Haptic *haptic; /* Assosciated haptic. */ -} SDL_hapticlist[MAX_HAPTICS]; + struct SDL_hapticlist_item *next; +} SDL_hapticlist_item; /* @@ -73,7 +81,9 @@ struct haptic_hweffect struct ff_effect effect; /* The linux kernel effect structure. */ }; - +static SDL_hapticlist_item *SDL_hapticlist = NULL; +static SDL_hapticlist_item *SDL_hapticlist_tail = NULL; +static int numhaptics = 0; #define test_bit(nr, addr) \ (((1UL << ((nr) & 31)) & (((const unsigned int *) addr)[(nr) >> 5])) != 0) @@ -147,15 +157,8 @@ int SDL_SYS_HapticInit(void) { const char joydev_pattern[] = "/dev/input/event%d"; - dev_t dev_nums[MAX_HAPTICS]; char path[PATH_MAX]; - struct stat sb; - int fd; - int i, j, k; - int duplicate; - int numhaptics; - - numhaptics = 0; + int i, j; /* * Limit amount of checks to MAX_HAPTICS since we may or may not have @@ -165,44 +168,189 @@ SDL_SYS_HapticInit(void) for (j = 0; j < MAX_HAPTICS; ++j) { snprintf(path, PATH_MAX, joydev_pattern, i++); + MaybeAddDevice(path); + } + +#if SDL_USE_LIBUDEV + if (SDL_UDEV_Init() < 0) { + return SDL_SetError("Could not initialize UDEV"); + } + + if ( SDL_UDEV_AddCallback(haptic_udev_callback) < 0) { + SDL_UDEV_Quit(); + return SDL_SetError("Could not setup haptic <-> udev callback"); + } +#endif /* SDL_USE_LIBUDEV */ + + return numhaptics; +} + +int +SDL_SYS_NumHaptics() +{ + return numhaptics; +} + +static SDL_hapticlist_item * +HapticByDevIndex(int device_index) +{ + SDL_hapticlist_item *item = SDL_hapticlist; + + if ((device_index < 0) || (device_index >= numhaptics)) { + return NULL; + } - /* check to see if file exists */ - if (stat(path, &sb) != 0) + while (device_index > 0) { + SDL_assert(item != NULL); + device_index--; + item = item->next; + } + + return item; +} + +#if SDL_USE_LIBUDEV +void haptic_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath) +{ + if (devpath == NULL || !(udev_class & SDL_UDEV_DEVICE_JOYSTICK)) { + return; + } + + switch( udev_type ) + { + case SDL_UDEV_DEVICEADDED: + MaybeAddDevice(devpath); break; + + case SDL_UDEV_DEVICEREMOVED: + MaybeRemoveDevice(devpath); + break; + + default: + break; + } + +} +#endif /* SDL_USE_LIBUDEV */ - /* check for duplicates */ - duplicate = 0; - for (k = 0; (k < numhaptics) && !duplicate; ++k) { - if (sb.st_rdev == dev_nums[k]) { - duplicate = 1; - } - } - if (duplicate) { - continue; +static int +MaybeAddDevice(const char *path) +{ + dev_t dev_nums[MAX_HAPTICS]; + struct stat sb; + int fd; + int k; + int duplicate; + int success; + SDL_hapticlist_item *item; + + + if (path == NULL) { + return -1; + } + + /* check to see if file exists */ + if (stat(path, &sb) != 0) { + return -1; + } + + /* check for duplicates */ + duplicate = 0; + for (k = 0; (k < numhaptics) && !duplicate; ++k) { + if (sb.st_rdev == dev_nums[k]) { + duplicate = 1; } + } + if (duplicate) { + return -1; + } - /* try to open */ - fd = open(path, O_RDWR, 0); - if (fd < 0) - continue; + /* try to open */ + fd = open(path, O_RDWR, 0); + if (fd < 0) { + return -1; + } #ifdef DEBUG_INPUT_EVENTS - printf("Checking %s\n", path); + printf("Checking %s\n", path); #endif - /* see if it works */ - if (EV_IsHaptic(fd) > 0) { - SDL_hapticlist[numhaptics].fname = SDL_strdup(path); - SDL_hapticlist[numhaptics].haptic = NULL; - dev_nums[numhaptics] = sb.st_rdev; - ++numhaptics; - } - close(fd); + /* see if it works */ + success = EV_IsHaptic(fd); + close(fd); + if (success <= 0) { + return -1; } + item = (SDL_hapticlist_item *) SDL_malloc(sizeof (SDL_hapticlist_item)); + if (item == NULL) { + return -1; + } + SDL_zerop(item); + item->fname = SDL_strdup(path); + if ( (item->fname == NULL) ) { + SDL_free(item->fname); + SDL_free(item); + return -1; + } + + /* TODO: should we add instance IDs? */ + if (SDL_hapticlist_tail == NULL) { + SDL_hapticlist = SDL_hapticlist_tail = item; + } else { + SDL_hapticlist_tail->next = item; + SDL_hapticlist_tail = item; + } + + dev_nums[numhaptics] = sb.st_rdev; + + ++numhaptics; + + /* !!! TODO: Send a haptic add event? */ + return numhaptics; } +#if SDL_USE_LIBUDEV +static int +MaybeRemoveDevice(const char* path) +{ + SDL_hapticlist_item *item; + SDL_hapticlist_item *prev = NULL; + + if (path == NULL) { + return -1; + } + + for (item = SDL_hapticlist; item != NULL; item = item->next) { + /* found it, remove it. */ + if (SDL_strcmp(path, item->fname) == 0) { + const int retval = item->haptic ? item->haptic->index : -1; + + if (prev != NULL) { + prev->next = item->next; + } else { + SDL_assert(SDL_hapticlist == item); + SDL_hapticlist = item->next; + } + if (item == SDL_hapticlist_tail) { + SDL_hapticlist_tail = prev; + } + + /* Need to decrement the haptic count */ + --numhaptics; + /* !!! TODO: Send a haptic remove event? */ + + SDL_free(item->fname); + SDL_free(item); + return retval; + } + prev = item; + } + + return -1; +} +#endif /* SDL_USE_LIBUDEV */ /* * Gets the name from a file descriptor. @@ -227,19 +375,21 @@ SDL_SYS_HapticNameFromFD(int fd) const char * SDL_SYS_HapticName(int index) { + SDL_hapticlist_item *item; int fd; const char *name; + item = HapticByDevIndex(index); /* Open the haptic device. */ name = NULL; - fd = open(SDL_hapticlist[index].fname, O_RDONLY, 0); + fd = open(item->fname, O_RDONLY, 0); if (fd >= 0) { name = SDL_SYS_HapticNameFromFD(fd); if (name == NULL) { /* No name found, return device character device */ - name = SDL_hapticlist[index].fname; + name = item->fname; } } close(fd); @@ -306,12 +456,14 @@ SDL_SYS_HapticOpen(SDL_Haptic * haptic) { int fd; int ret; + SDL_hapticlist_item *item; + item = HapticByDevIndex(haptic->index); /* Open the character device */ - fd = open(SDL_hapticlist[haptic->index].fname, O_RDWR, 0); + fd = open(item->fname, O_RDWR, 0); if (fd < 0) { return SDL_SetError("Haptic: Unable to open %s: %s", - SDL_hapticlist[haptic->index], strerror(errno)); + item->fname, strerror(errno)); } /* Try to create the haptic. */ @@ -321,7 +473,7 @@ SDL_SYS_HapticOpen(SDL_Haptic * haptic) } /* Set the fname. */ - haptic->hwdata->fname = SDL_hapticlist[haptic->index].fname; + haptic->hwdata->fname = item->fname; return 0; } @@ -333,24 +485,27 @@ int SDL_SYS_HapticMouse(void) { int fd; - int i; - - for (i = 0; i < SDL_numhaptics; i++) { + int device_index = 0; + SDL_hapticlist_item *item; + + for (item = SDL_hapticlist; item; item = item->next) { /* Open the device. */ - fd = open(SDL_hapticlist[i].fname, O_RDWR, 0); + fd = open(item->fname, O_RDWR, 0); if (fd < 0) { return SDL_SetError("Haptic: Unable to open %s: %s", - SDL_hapticlist[i], strerror(errno)); + item->fname, strerror(errno)); } /* Is it a mouse? */ if (EV_IsMouse(fd)) { close(fd); - return i; + return device_index; } close(fd); + + ++device_index; } return -1; @@ -388,22 +543,21 @@ SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) int SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) { - int i; + int device_index = 0; int fd; int ret; + SDL_hapticlist_item *item; /* Find the joystick in the haptic list. */ - for (i = 0; i < MAX_HAPTICS; i++) { - if (SDL_hapticlist[i].fname != NULL) { - if (SDL_strcmp(SDL_hapticlist[i].fname, joystick->hwdata->fname) - == 0) { - haptic->index = i; - break; - } - } + for (item = SDL_hapticlist; item; item = item->next) { + if (SDL_strcmp(item->fname, joystick->hwdata->fname) == 0) { + haptic->index = device_index; + break; + } + ++device_index; } - if (i >= MAX_HAPTICS) { + if (device_index >= MAX_HAPTICS) { return SDL_SetError("Haptic: Joystick doesn't have Haptic capabilities"); } @@ -417,7 +571,7 @@ SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) return -1; } - haptic->hwdata->fname = SDL_hapticlist[haptic->index].fname; + haptic->hwdata->fname = item->fname; return 0; } @@ -454,15 +608,23 @@ SDL_SYS_HapticClose(SDL_Haptic * haptic) void SDL_SYS_HapticQuit(void) { - int i; + SDL_hapticlist_item *item = NULL; + SDL_hapticlist_item *next = NULL; - for (i = 0; SDL_hapticlist[i].fname != NULL; i++) { + for (item = SDL_hapticlist; item; item = next) { + next = item->next; /* Opened and not closed haptics are leaked, this is on purpose. * Close your haptic devices after usage. */ - - SDL_free(SDL_hapticlist[i].fname); + SDL_free(item->fname); + item->fname = NULL; } - SDL_hapticlist[0].fname = NULL; + +#if SDL_USE_LIBUDEV + SDL_UDEV_DelCallback(haptic_udev_callback); + SDL_UDEV_Quit(); +#endif /* SDL_USE_LIBUDEV */ + + numhaptics = 0; }