Backport #7697 to SDL 2

Backport fixes from #8349

Include changes from #8357
This commit is contained in:
KWottrich 2023-10-02 13:45:34 -05:00 committed by Sam Lantinga
parent c4a169a6fc
commit 25b0081367
2 changed files with 470 additions and 47 deletions

View file

@ -162,10 +162,20 @@ typedef struct SDL_joylist_item
SDL_GamepadMapping *mapping; SDL_GamepadMapping *mapping;
} SDL_joylist_item; } SDL_joylist_item;
/* A linked list of available gamepad sensors */
typedef struct SDL_sensorlist_item
{
char *path; /* "/dev/input/event2" or whatever */
dev_t devnum;
struct joystick_hwdata *hwdata;
struct SDL_sensorlist_item *next;
} SDL_sensorlist_item;
static SDL_bool SDL_classic_joysticks = SDL_FALSE; static SDL_bool SDL_classic_joysticks = SDL_FALSE;
static SDL_joylist_item *SDL_joylist = NULL; static SDL_joylist_item *SDL_joylist = NULL;
static SDL_joylist_item *SDL_joylist_tail = NULL; static SDL_joylist_item *SDL_joylist_tail = NULL;
static int numjoysticks = 0; static int numjoysticks = 0;
static SDL_sensorlist_item *SDL_sensorlist = NULL;
static int inotify_fd = -1; static int inotify_fd = -1;
static Uint32 last_joy_detect_time; static Uint32 last_joy_detect_time;
@ -205,13 +215,12 @@ static SDL_bool IsVirtualJoystick(Uint16 vendor, Uint16 product, Uint16 version,
} }
#endif /* SDL_JOYSTICK_HIDAPI */ #endif /* SDL_JOYSTICK_HIDAPI */
static int GuessIsJoystick(int fd) static int GuessDeviceClass(int fd)
{ {
unsigned long evbit[NBITS(EV_MAX)] = { 0 }; unsigned long evbit[NBITS(EV_MAX)] = { 0 };
unsigned long keybit[NBITS(KEY_MAX)] = { 0 }; unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
unsigned long absbit[NBITS(ABS_MAX)] = { 0 }; unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
unsigned long relbit[NBITS(REL_MAX)] = { 0 }; unsigned long relbit[NBITS(REL_MAX)] = { 0 };
int devclass;
if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) || if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) ||
(ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) || (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) ||
@ -220,9 +229,21 @@ static int GuessIsJoystick(int fd)
return 0; return 0;
} }
devclass = SDL_EVDEV_GuessDeviceClass(evbit, absbit, keybit, relbit); return SDL_EVDEV_GuessDeviceClass(evbit, absbit, keybit, relbit);
}
if (devclass & SDL_UDEV_DEVICE_JOYSTICK) { static int GuessIsJoystick(int fd)
{
if (GuessDeviceClass(fd) & SDL_UDEV_DEVICE_JOYSTICK) {
return 1;
}
return 0;
}
static int GuessIsSensor(int fd)
{
if (GuessDeviceClass(fd) & SDL_UDEV_DEVICE_ACCELEROMETER) {
return 1; return 1;
} }
@ -285,6 +306,23 @@ static int IsJoystick(const char *path, int fd, char **name_return, SDL_Joystick
return 1; return 1;
} }
static int IsSensor(const char *path, int fd)
{
struct input_id inpid;
if (ioctl(fd, EVIOCGID, &inpid) < 0) {
return 0;
}
if (inpid.vendor == USB_VENDOR_NINTENDO && inpid.product == USB_PRODUCT_NINTENDO_WII_REMOTE) {
/* Wii extension controls */
/* These may create 3 sensor devices but we only support reading from 1: ignore them */
return 0;
}
return GuessIsSensor(fd);
}
#if SDL_USE_LIBUDEV #if SDL_USE_LIBUDEV
static void joystick_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath) static void joystick_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath)
{ {
@ -331,14 +369,20 @@ static void FreeJoylistItem(SDL_joylist_item *item)
SDL_free(item); SDL_free(item);
} }
static void FreeSensorlistItem(SDL_sensorlist_item *item)
{
SDL_free(item->path);
SDL_free(item);
}
static int MaybeAddDevice(const char *path) static int MaybeAddDevice(const char *path)
{ {
struct stat sb; struct stat sb;
int fd = -1; int fd = -1;
int isstick = 0;
char *name = NULL; char *name = NULL;
SDL_JoystickGUID guid; SDL_JoystickGUID guid;
SDL_joylist_item *item; SDL_joylist_item *item;
SDL_sensorlist_item *item_sensor;
if (path == NULL) { if (path == NULL) {
return -1; return -1;
@ -354,6 +398,11 @@ static int MaybeAddDevice(const char *path)
return -1; /* already have this one */ return -1; /* already have this one */
} }
} }
for (item_sensor = SDL_sensorlist; item_sensor != NULL; item_sensor = item_sensor->next) {
if (sb.st_rdev == item_sensor->devnum) {
return -1; /* already have this one */
}
}
fd = open(path, O_RDONLY | O_CLOEXEC, 0); fd = open(path, O_RDONLY | O_CLOEXEC, 0);
if (fd < 0) { if (fd < 0) {
@ -364,42 +413,66 @@ static int MaybeAddDevice(const char *path)
SDL_Log("Checking %s\n", path); SDL_Log("Checking %s\n", path);
#endif #endif
isstick = IsJoystick(path, fd, &name, &guid); if (IsJoystick(path, fd, &name, &guid)) {
#ifdef DEBUG_INPUT_EVENTS
SDL_Log("found joystick: %s\n", path);
#endif
close(fd);
item = (SDL_joylist_item *)SDL_calloc(1, sizeof(SDL_joylist_item));
if (item == NULL) {
SDL_free(name);
return -1;
}
item->devnum = sb.st_rdev;
item->path = SDL_strdup(path);
item->name = name;
item->guid = guid;
if ((item->path == NULL) || (item->name == NULL)) {
FreeJoylistItem(item);
return -1;
}
item->device_instance = SDL_GetNextJoystickInstanceID();
if (SDL_joylist_tail == NULL) {
SDL_joylist = SDL_joylist_tail = item;
} else {
SDL_joylist_tail->next = item;
SDL_joylist_tail = item;
}
/* Need to increment the joystick count before we post the event */
++numjoysticks;
SDL_PrivateJoystickAdded(item->device_instance);
return numjoysticks;
}
if (IsSensor(path, fd)) {
#ifdef DEBUG_INPUT_EVENTS
SDL_Log("found sensor: %s\n", path);
#endif
close(fd);
item_sensor = (SDL_sensorlist_item *)SDL_calloc(1, sizeof(SDL_sensorlist_item));
if (item_sensor == NULL) {
return -1;
}
item_sensor->devnum = sb.st_rdev;
item_sensor->path = SDL_strdup(path);
if (item_sensor->path == NULL) {
FreeSensorlistItem(item_sensor);
return -1;
}
item_sensor->next = SDL_sensorlist;
SDL_sensorlist = item_sensor;
return -1;
}
close(fd); close(fd);
if (!isstick) { return -1;
return -1;
}
item = (SDL_joylist_item *)SDL_calloc(1, sizeof(SDL_joylist_item));
if (item == NULL) {
SDL_free(name);
return -1;
}
item->devnum = sb.st_rdev;
item->path = SDL_strdup(path);
item->name = name;
item->guid = guid;
if ((item->path == NULL) || (item->name == NULL)) {
FreeJoylistItem(item);
return -1;
}
item->device_instance = SDL_GetNextJoystickInstanceID();
if (SDL_joylist_tail == NULL) {
SDL_joylist = SDL_joylist_tail = item;
} else {
SDL_joylist_tail->next = item;
SDL_joylist_tail = item;
}
/* Need to increment the joystick count before we post the event */
++numjoysticks;
SDL_PrivateJoystickAdded(item->device_instance);
return numjoysticks;
} }
static void RemoveJoylistItem(SDL_joylist_item *item, SDL_joylist_item *prev) static void RemoveJoylistItem(SDL_joylist_item *item, SDL_joylist_item *prev)
@ -426,10 +499,30 @@ static void RemoveJoylistItem(SDL_joylist_item *item, SDL_joylist_item *prev)
FreeJoylistItem(item); FreeJoylistItem(item);
} }
static void RemoveSensorlistItem(SDL_sensorlist_item *item, SDL_sensorlist_item *prev)
{
if (item->hwdata) {
item->hwdata->item_sensor = NULL;
}
if (prev != NULL) {
prev->next = item->next;
} else {
SDL_assert(SDL_sensorlist == item);
SDL_sensorlist = item->next;
}
/* Do not call SDL_PrivateJoystickRemoved here as RemoveJoylistItem will do it,
* assuming both sensor and joy item are removed at the same time */
FreeSensorlistItem(item);
}
static int MaybeRemoveDevice(const char *path) static int MaybeRemoveDevice(const char *path)
{ {
SDL_joylist_item *item; SDL_joylist_item *item;
SDL_joylist_item *prev = NULL; SDL_joylist_item *prev = NULL;
SDL_sensorlist_item *item_sensor;
SDL_sensorlist_item *prev_sensor = NULL;
if (path == NULL) { if (path == NULL) {
return -1; return -1;
@ -444,6 +537,14 @@ static int MaybeRemoveDevice(const char *path)
} }
prev = item; prev = item;
} }
for (item_sensor = SDL_sensorlist; item_sensor != NULL; item_sensor = item_sensor->next) {
/* found it, remove it. */
if (SDL_strcmp(path, item_sensor->path) == 0) {
RemoveSensorlistItem(item_sensor, prev_sensor);
return -1;
}
prev_sensor = item_sensor;
}
return -1; return -1;
} }
@ -452,6 +553,8 @@ static void HandlePendingRemovals(void)
{ {
SDL_joylist_item *prev = NULL; SDL_joylist_item *prev = NULL;
SDL_joylist_item *item = SDL_joylist; SDL_joylist_item *item = SDL_joylist;
SDL_sensorlist_item *prev_sensor = NULL;
SDL_sensorlist_item *item_sensor = SDL_sensorlist;
while (item != NULL) { while (item != NULL) {
if (item->hwdata && item->hwdata->gone) { if (item->hwdata && item->hwdata->gone) {
@ -467,6 +570,21 @@ static void HandlePendingRemovals(void)
item = item->next; item = item->next;
} }
} }
while (item_sensor != NULL) {
if (item_sensor->hwdata && item_sensor->hwdata->sensor_gone) {
RemoveSensorlistItem(item_sensor, prev_sensor);
if (prev_sensor != NULL) {
item_sensor = prev_sensor->next;
} else {
item_sensor = SDL_sensorlist;
}
} else {
prev_sensor = item_sensor;
item_sensor = item_sensor->next;
}
}
} }
static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickGUID guid, int *device_instance) static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickGUID guid, int *device_instance)
@ -962,7 +1080,7 @@ static SDL_bool GuessIfAxesAreDigitalHat(struct input_absinfo *absinfo_x, struct
return SDL_FALSE; return SDL_FALSE;
} }
static void ConfigJoystick(SDL_Joystick *joystick, int fd) static void ConfigJoystick(SDL_Joystick *joystick, int fd, int fd_sensor)
{ {
int i, t; int i, t;
unsigned long keybit[NBITS(KEY_MAX)] = { 0 }; unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
@ -1153,6 +1271,45 @@ static void ConfigJoystick(SDL_Joystick *joystick, int fd)
} }
} }
/* Sensors are only available through the new unified event API */
if (fd_sensor >= 0 && (ioctl(fd_sensor, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0)) {
if (test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit) && test_bit(ABS_Z, absbit)) {
joystick->hwdata->has_accelerometer = SDL_TRUE;
for (i = 0; i < 3; ++i) {
struct input_absinfo absinfo;
if (ioctl(fd_sensor, EVIOCGABS(ABS_X + i), &absinfo) < 0) {
joystick->hwdata->has_accelerometer = SDL_FALSE;
break; /* do not report an accelerometer if we can't read all axes */
}
joystick->hwdata->accelerometer_scale[i] = absinfo.resolution;
#ifdef DEBUG_INPUT_EVENTS
SDL_Log("Joystick has accelerometer axis: 0x%.2x\n", ABS_X + i);
SDL_Log("Values = { val:%d, min:%d, max:%d, fuzz:%d, flat:%d, res:%d }\n",
absinfo.value, absinfo.minimum, absinfo.maximum,
absinfo.fuzz, absinfo.flat, absinfo.resolution);
#endif /* DEBUG_INPUT_EVENTS */
}
}
if (test_bit(ABS_RX, absbit) && test_bit(ABS_RY, absbit) && test_bit(ABS_RZ, absbit)) {
joystick->hwdata->has_gyro = SDL_TRUE;
for (i = 0; i < 3; ++i) {
struct input_absinfo absinfo;
if (ioctl(fd_sensor, EVIOCGABS(ABS_RX + i), &absinfo) < 0) {
joystick->hwdata->has_gyro = SDL_FALSE;
break; /* do not report a gyro if we can't read all axes */
}
joystick->hwdata->gyro_scale[i] = absinfo.resolution;
#ifdef DEBUG_INPUT_EVENTS
SDL_Log("Joystick has gyro axis: 0x%.2x\n", ABS_RX + i);
SDL_Log("Values = { val:%d, min:%d, max:%d, fuzz:%d, flat:%d, res:%d }\n",
absinfo.value, absinfo.minimum, absinfo.maximum,
absinfo.fuzz, absinfo.flat, absinfo.resolution);
#endif /* DEBUG_INPUT_EVENTS */
}
}
}
/* Allocate data to keep track of these thingamajigs */ /* Allocate data to keep track of these thingamajigs */
if (joystick->nhats > 0) { if (joystick->nhats > 0) {
if (allocate_hatdata(joystick) < 0) { if (allocate_hatdata(joystick) < 0) {
@ -1180,11 +1337,12 @@ static void ConfigJoystick(SDL_Joystick *joystick, int fd)
without adding an opened SDL_Joystick object to the system. without adding an opened SDL_Joystick object to the system.
This expects `joystick->hwdata` to be allocated and will not free it This expects `joystick->hwdata` to be allocated and will not free it
on error. Returns -1 on error, 0 on success. */ on error. Returns -1 on error, 0 on success. */
static int PrepareJoystickHwdata(SDL_Joystick *joystick, SDL_joylist_item *item) static int PrepareJoystickHwdata(SDL_Joystick *joystick, SDL_joylist_item *item, SDL_sensorlist_item *item_sensor)
{ {
SDL_AssertJoysticksLocked(); SDL_AssertJoysticksLocked();
joystick->hwdata->item = item; joystick->hwdata->item = item;
joystick->hwdata->item_sensor = item_sensor;
joystick->hwdata->guid = item->guid; joystick->hwdata->guid = item->guid;
joystick->hwdata->effect.id = -1; joystick->hwdata->effect.id = -1;
joystick->hwdata->m_bSteamController = item->m_bSteamController; joystick->hwdata->m_bSteamController = item->m_bSteamController;
@ -1193,12 +1351,14 @@ static int PrepareJoystickHwdata(SDL_Joystick *joystick, SDL_joylist_item *item)
if (item->m_bSteamController) { if (item->m_bSteamController) {
joystick->hwdata->fd = -1; joystick->hwdata->fd = -1;
joystick->hwdata->fd_sensor = -1;
SDL_GetSteamControllerInputs(&joystick->nbuttons, SDL_GetSteamControllerInputs(&joystick->nbuttons,
&joystick->naxes, &joystick->naxes,
&joystick->nhats); &joystick->nhats);
} else { } else {
int fd = -1, fd_sensor = -1;
/* Try read-write first, so we can do rumble */ /* Try read-write first, so we can do rumble */
int fd = open(item->path, O_RDWR | O_CLOEXEC, 0); fd = open(item->path, O_RDWR | O_CLOEXEC, 0);
if (fd < 0) { if (fd < 0) {
/* Try read-only again, at least we'll get events in this case */ /* Try read-only again, at least we'll get events in this case */
fd = open(item->path, O_RDONLY | O_CLOEXEC, 0); fd = open(item->path, O_RDONLY | O_CLOEXEC, 0);
@ -1206,23 +1366,86 @@ static int PrepareJoystickHwdata(SDL_Joystick *joystick, SDL_joylist_item *item)
if (fd < 0) { if (fd < 0) {
return SDL_SetError("Unable to open %s", item->path); return SDL_SetError("Unable to open %s", item->path);
} }
/* If opening sensor fail, continue with buttons and axes only */
if (item_sensor != NULL) {
fd_sensor = open(item_sensor->path, O_RDONLY | O_CLOEXEC, 0);
}
joystick->hwdata->fd = fd; joystick->hwdata->fd = fd;
joystick->hwdata->fd_sensor = fd_sensor;
joystick->hwdata->fname = SDL_strdup(item->path); joystick->hwdata->fname = SDL_strdup(item->path);
if (joystick->hwdata->fname == NULL) { if (joystick->hwdata->fname == NULL) {
close(fd); close(fd);
if (fd_sensor >= 0) {
close(fd_sensor);
}
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
/* Set the joystick to non-blocking read mode */ /* Set the joystick to non-blocking read mode */
fcntl(fd, F_SETFL, O_NONBLOCK); fcntl(fd, F_SETFL, O_NONBLOCK);
if (fd_sensor >= 0) {
fcntl(fd_sensor, F_SETFL, O_NONBLOCK);
}
/* Get the number of buttons and axes on the joystick */ /* Get the number of buttons and axes on the joystick */
ConfigJoystick(joystick, fd); ConfigJoystick(joystick, fd, fd_sensor);
} }
return 0; return 0;
} }
static SDL_sensorlist_item *GetSensor(SDL_joylist_item *item)
{
SDL_sensorlist_item *item_sensor;
char uniq_item[128];
int fd_item = -1;
if (item == NULL || SDL_sensorlist == NULL) {
return NULL;
}
SDL_memset(uniq_item, 0, sizeof(uniq_item));
fd_item = open(item->path, O_RDONLY | O_CLOEXEC, 0);
if (fd_item < 0) {
return NULL;
}
if (ioctl(fd_item, EVIOCGUNIQ(sizeof(uniq_item) - 1), &uniq_item) < 0) {
return NULL;
}
close(fd_item);
#ifdef DEBUG_INPUT_EVENTS
SDL_Log("Joystick UNIQ: %s\n", uniq_item);
#endif /* DEBUG_INPUT_EVENTS */
for (item_sensor = SDL_sensorlist; item_sensor != NULL; item_sensor = item_sensor->next) {
char uniq_sensor[128];
int fd_sensor = -1;
if (item_sensor->hwdata != NULL) {
/* already associated with another joystick */
continue;
}
SDL_memset(uniq_sensor, 0, sizeof(uniq_sensor));
fd_sensor = open(item_sensor->path, O_RDONLY | O_CLOEXEC, 0);
if (fd_sensor < 0) {
continue;
}
if (ioctl(fd_sensor, EVIOCGUNIQ(sizeof(uniq_sensor) - 1), &uniq_sensor) < 0) {
close(fd_sensor);
continue;
}
close(fd_sensor);
#ifdef DEBUG_INPUT_EVENTS
SDL_Log("Sensor UNIQ: %s\n", uniq_sensor);
#endif /* DEBUG_INPUT_EVENTS */
if (SDL_strcmp(uniq_item, uniq_sensor) == 0) {
return item_sensor;
}
}
return NULL;
}
/* Function to open a joystick for use. /* Function to open a joystick for use.
The joystick to open is specified by the device index. The joystick to open is specified by the device index.
This should fill the nbuttons and naxes fields of the joystick structure. This should fill the nbuttons and naxes fields of the joystick structure.
@ -1231,6 +1454,7 @@ static int PrepareJoystickHwdata(SDL_Joystick *joystick, SDL_joylist_item *item)
static int LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index) static int LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index)
{ {
SDL_joylist_item *item = JoystickByDevIndex(device_index); SDL_joylist_item *item = JoystickByDevIndex(device_index);
SDL_sensorlist_item *item_sensor = GetSensor(item);
SDL_AssertJoysticksLocked(); SDL_AssertJoysticksLocked();
@ -1245,18 +1469,34 @@ static int LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index)
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
if (PrepareJoystickHwdata(joystick, item) == -1) { if (PrepareJoystickHwdata(joystick, item, item_sensor) == -1) {
SDL_free(joystick->hwdata); SDL_free(joystick->hwdata);
joystick->hwdata = NULL; joystick->hwdata = NULL;
return -1; /* SDL_SetError will already have been called */ return -1; /* SDL_SetError will already have been called */
} }
SDL_assert(item->hwdata == NULL); SDL_assert(item->hwdata == NULL);
SDL_assert(!item_sensor || item_sensor->hwdata == NULL);
item->hwdata = joystick->hwdata; item->hwdata = joystick->hwdata;
if (item_sensor != NULL) {
item_sensor->hwdata = joystick->hwdata;
}
/* mark joystick as fresh and ready */ /* mark joystick as fresh and ready */
joystick->hwdata->fresh = SDL_TRUE; joystick->hwdata->fresh = SDL_TRUE;
if (joystick->hwdata->has_gyro) {
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 0.0f);
}
if (joystick->hwdata->has_accelerometer) {
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 0.0f);
}
if (joystick->hwdata->fd_sensor >= 0) {
/* Don't keep fd_sensor opened while sensor is disabled */
close(joystick->hwdata->fd_sensor);
joystick->hwdata->fd_sensor = -1;
}
return 0; return 0;
} }
@ -1333,7 +1573,30 @@ static int LINUX_JoystickSendEffect(SDL_Joystick *joystick, const void *data, in
static int LINUX_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) static int LINUX_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled)
{ {
return SDL_Unsupported(); if (!joystick->hwdata->has_accelerometer && !joystick->hwdata->has_gyro) {
return SDL_Unsupported();
}
if (enabled == joystick->hwdata->report_sensor) {
return 0;
}
if (enabled) {
if (joystick->hwdata->item_sensor == NULL) {
return SDL_SetError("Sensors unplugged.");
}
joystick->hwdata->fd_sensor = open(joystick->hwdata->item_sensor->path, O_RDONLY | O_CLOEXEC, 0);
if (joystick->hwdata->fd_sensor < 0) {
return SDL_SetError("Couldn't open sensor file %s.", joystick->hwdata->item_sensor->path);
}
fcntl(joystick->hwdata->fd_sensor, F_SETFL, O_NONBLOCK);
} else {
SDL_assert(joystick->hwdata->fd_sensor >= 0);
close(joystick->hwdata->fd_sensor);
joystick->hwdata->fd_sensor = -1;
}
joystick->hwdata->report_sensor = enabled;
return 0;
} }
static void HandleHat(SDL_Joystick *stick, int hatidx, int axis, int value) static void HandleHat(SDL_Joystick *stick, int hatidx, int axis, int value)
@ -1486,6 +1749,39 @@ static void PollAllValues(SDL_Joystick *joystick)
/* Joyballs are relative input, so there's no poll state. Events only! */ /* Joyballs are relative input, so there's no poll state. Events only! */
} }
static void PollAllSensors(SDL_Joystick *joystick)
{
struct input_absinfo absinfo;
int i;
SDL_assert(joystick->hwdata->fd_sensor >= 0);
if (joystick->hwdata->has_gyro) {
float data[3] = {0.0f, 0.0f, 0.0f};
for (i = 0; i < 3; i++) {
if (ioctl(joystick->hwdata->fd_sensor, EVIOCGABS(ABS_RX + i), &absinfo) >= 0) {
data[i] = absinfo.value * (M_PI / 180.f) / joystick->hwdata->gyro_scale[i];
#ifdef DEBUG_INPUT_EVENTS
SDL_Log("Joystick : Re-read Gyro (axis %d) val= %f\n", i, data[i]);
#endif
}
}
SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_GYRO, joystick->hwdata->sensor_tick, data, 3);
}
if (joystick->hwdata->has_accelerometer) {
float data[3] = {0.0f, 0.0f, 0.0f};
for (i = 0; i < 3; i++) {
if (ioctl(joystick->hwdata->fd_sensor, EVIOCGABS(ABS_X + i), &absinfo) >= 0) {
data[i] = absinfo.value * SDL_STANDARD_GRAVITY / joystick->hwdata->accelerometer_scale[i];
#ifdef DEBUG_INPUT_EVENTS
SDL_Log("Joystick : Re-read Accelerometer (axis %d) val= %f\n", i, data[i]);
#endif
}
}
SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_ACCEL, joystick->hwdata->sensor_tick, data, 3);
}
}
static void HandleInputEvents(SDL_Joystick *joystick) static void HandleInputEvents(SDL_Joystick *joystick)
{ {
struct input_event events[32]; struct input_event events[32];
@ -1495,9 +1791,14 @@ static void HandleInputEvents(SDL_Joystick *joystick)
if (joystick->hwdata->fresh) { if (joystick->hwdata->fresh) {
PollAllValues(joystick); PollAllValues(joystick);
if (joystick->hwdata->report_sensor) {
PollAllSensors(joystick);
}
joystick->hwdata->fresh = SDL_FALSE; joystick->hwdata->fresh = SDL_FALSE;
} }
errno = 0;
while ((len = read(joystick->hwdata->fd, events, sizeof(events))) > 0) { while ((len = read(joystick->hwdata->fd, events, sizeof(events))) > 0) {
len /= sizeof(events[0]); len /= sizeof(events[0]);
for (i = 0; i < len; ++i) { for (i = 0; i < len; ++i) {
@ -1577,6 +1878,96 @@ static void HandleInputEvents(SDL_Joystick *joystick)
if (errno == ENODEV) { if (errno == ENODEV) {
/* We have to wait until the JoystickDetect callback to remove this */ /* We have to wait until the JoystickDetect callback to remove this */
joystick->hwdata->gone = SDL_TRUE; joystick->hwdata->gone = SDL_TRUE;
errno = 0;
}
if (joystick->hwdata->report_sensor) {
SDL_assert(joystick->hwdata->fd_sensor >= 0);
while ((len = read(joystick->hwdata->fd_sensor, events, sizeof(events))) > 0) {
len /= sizeof(events[0]);
for (i = 0; i < len; ++i) {
unsigned int j;
struct input_event *event = &events[i];
code = event->code;
/* If the kernel sent a SYN_DROPPED, we are supposed to ignore the
rest of the packet (the end of it signified by a SYN_REPORT) */
if (joystick->hwdata->recovering_from_dropped_sensor &&
((event->type != EV_SYN) || (code != SYN_REPORT))) {
continue;
}
switch (event->type) {
case EV_KEY:
SDL_assert(0);
break;
case EV_ABS:
switch (code) {
case ABS_X:
case ABS_Y:
case ABS_Z:
j = code - ABS_X;
joystick->hwdata->accel_data[j] = event->value * SDL_STANDARD_GRAVITY
/ joystick->hwdata->accelerometer_scale[j];
break;
case ABS_RX:
case ABS_RY:
case ABS_RZ:
j = code - ABS_RX;
joystick->hwdata->gyro_data[j] = event->value * (M_PI / 180.f)
/ joystick->hwdata->gyro_scale[j];
break;
}
break;
case EV_MSC:
if (code == MSC_TIMESTAMP) {
Sint32 tick = event->value;
Sint32 delta;
if (joystick->hwdata->last_tick < tick) {
delta = (tick - joystick->hwdata->last_tick);
} else {
delta = (SDL_MAX_SINT32 - joystick->hwdata->last_tick + tick + 1);
}
joystick->hwdata->sensor_tick += delta;
joystick->hwdata->last_tick = tick;
}
break;
case EV_SYN:
switch (code) {
case SYN_DROPPED:
#ifdef DEBUG_INPUT_EVENTS
SDL_Log("Event SYN_DROPPED detected\n");
#endif
joystick->hwdata->recovering_from_dropped_sensor = SDL_TRUE;
break;
case SYN_REPORT:
if (joystick->hwdata->recovering_from_dropped_sensor) {
joystick->hwdata->recovering_from_dropped_sensor = SDL_FALSE;
PollAllSensors(joystick); /* try to sync up to current state now */
} else {
SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_GYRO,
joystick->hwdata->sensor_tick,
joystick->hwdata->gyro_data, 3);
SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_ACCEL,
joystick->hwdata->sensor_tick,
joystick->hwdata->accel_data, 3);
}
break;
default:
break;
}
default:
break;
}
}
}
}
if (errno == ENODEV) {
/* We have to wait until the JoystickDetect callback to remove this */
joystick->hwdata->sensor_gone = SDL_TRUE;
} }
} }
@ -1670,9 +2061,15 @@ static void LINUX_JoystickClose(SDL_Joystick *joystick)
if (joystick->hwdata->fd >= 0) { if (joystick->hwdata->fd >= 0) {
close(joystick->hwdata->fd); close(joystick->hwdata->fd);
} }
if (joystick->hwdata->fd_sensor >= 0) {
close(joystick->hwdata->fd_sensor);
}
if (joystick->hwdata->item) { if (joystick->hwdata->item) {
joystick->hwdata->item->hwdata = NULL; joystick->hwdata->item->hwdata = NULL;
} }
if (joystick->hwdata->item_sensor) {
joystick->hwdata->item_sensor->hwdata = NULL;
}
SDL_free(joystick->hwdata->key_pam); SDL_free(joystick->hwdata->key_pam);
SDL_free(joystick->hwdata->abs_pam); SDL_free(joystick->hwdata->abs_pam);
SDL_free(joystick->hwdata->hats); SDL_free(joystick->hwdata->hats);
@ -1687,6 +2084,8 @@ static void LINUX_JoystickQuit(void)
{ {
SDL_joylist_item *item = NULL; SDL_joylist_item *item = NULL;
SDL_joylist_item *next = NULL; SDL_joylist_item *next = NULL;
SDL_sensorlist_item *item_sensor = NULL;
SDL_sensorlist_item *next_sensor = NULL;
if (inotify_fd >= 0) { if (inotify_fd >= 0) {
close(inotify_fd); close(inotify_fd);
@ -1697,8 +2096,13 @@ static void LINUX_JoystickQuit(void)
next = item->next; next = item->next;
FreeJoylistItem(item); FreeJoylistItem(item);
} }
for (item_sensor = SDL_sensorlist; item_sensor; item_sensor = next_sensor) {
next_sensor = item_sensor->next;
FreeSensorlistItem(item_sensor);
}
SDL_joylist = SDL_joylist_tail = NULL; SDL_joylist = SDL_joylist_tail = NULL;
SDL_sensorlist = NULL;
numjoysticks = 0; numjoysticks = 0;
@ -1769,7 +2173,7 @@ static SDL_bool LINUX_JoystickGetGamepadMapping(int device_index, SDL_GamepadMap
item->checked_mapping = SDL_TRUE; item->checked_mapping = SDL_TRUE;
if (PrepareJoystickHwdata(joystick, item) == -1) { if (PrepareJoystickHwdata(joystick, item, NULL) == -1) {
SDL_free(joystick->hwdata); SDL_free(joystick->hwdata);
SDL_free(joystick); SDL_free(joystick);
return SDL_FALSE; /* SDL_SetError will already have been called */ return SDL_FALSE; /* SDL_SetError will already have been called */

View file

@ -25,12 +25,16 @@
#include <linux/input.h> #include <linux/input.h>
struct SDL_joylist_item; struct SDL_joylist_item;
struct SDL_sensorlist_item;
/* The private structure used to keep track of a joystick */ /* The private structure used to keep track of a joystick */
struct joystick_hwdata struct joystick_hwdata
{ {
int fd; int fd;
/* linux driver creates a separate device for gyro/accelerometer */
int fd_sensor;
struct SDL_joylist_item *item; struct SDL_joylist_item *item;
struct SDL_sensorlist_item *item_sensor;
SDL_JoystickGUID guid; SDL_JoystickGUID guid;
char *fname; /* Used in haptic subsystem */ char *fname; /* Used in haptic subsystem */
@ -55,6 +59,8 @@ struct joystick_hwdata
Uint8 abs_map[ABS_MAX]; Uint8 abs_map[ABS_MAX];
SDL_bool has_key[KEY_MAX]; SDL_bool has_key[KEY_MAX];
SDL_bool has_abs[ABS_MAX]; SDL_bool has_abs[ABS_MAX];
SDL_bool has_accelerometer;
SDL_bool has_gyro;
/* Support for the classic joystick interface */ /* Support for the classic joystick interface */
SDL_bool classic; SDL_bool classic;
@ -74,8 +80,20 @@ struct joystick_hwdata
float scale; float scale;
} abs_correct[ABS_MAX]; } abs_correct[ABS_MAX];
float accelerometer_scale[3];
float gyro_scale[3];
/* Each axis is read independently, if we don't get all axis this call to
* LINUX_JoystickUpdateupdate(), store them for the next one */
float gyro_data[3];
float accel_data[3];
Uint64 sensor_tick;
Sint32 last_tick;
SDL_bool report_sensor;
SDL_bool fresh; SDL_bool fresh;
SDL_bool recovering_from_dropped; SDL_bool recovering_from_dropped;
SDL_bool recovering_from_dropped_sensor;
/* Steam Controller support */ /* Steam Controller support */
SDL_bool m_bSteamController; SDL_bool m_bSteamController;
@ -92,6 +110,7 @@ struct joystick_hwdata
/* Set when gamepad is pending removal due to ENODEV read error */ /* Set when gamepad is pending removal due to ENODEV read error */
SDL_bool gone; SDL_bool gone;
SDL_bool sensor_gone;
}; };
#endif /* SDL_sysjoystick_c_h_ */ #endif /* SDL_sysjoystick_c_h_ */