SDL-mirror/src/joystick/linux/SDL_sysjoystick.c

604 lines
18 KiB
C
Raw Normal View History

/*
2011-04-08 13:03:26 -07:00
Simple DirectMedia Layer
2011-12-31 09:28:07 -05:00
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
2011-04-08 13:03:26 -07:00
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_config.h"
#ifdef SDL_JOYSTICK_LINUX
#ifndef SDL_INPUT_LINUXEV
#error SDL now requires a Linux 2.4+ kernel with /dev/input/event support.
#endif
/* This is the system specific header for the SDL joystick API */
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <limits.h> /* For the definition of PATH_MAX */
#include <linux/joystick.h>
#include "SDL_joystick.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
/* A list of available joysticks */
static struct
{
char *fname;
} SDL_joylist[MAX_JOYSTICKS];
#define test_bit(nr, addr) \
(((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)
{
unsigned long evbit[NBITS(EV_MAX)] = { 0 };
unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) ||
(ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) ||
(ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) {
return (0);
}
if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) &&
test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit))) {
return 0;
}
return (1);
}
static int SDL_SYS_numjoysticks = 0;
/* 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;
envcopy = SDL_strdup(SDL_getenv("SDL_JOYSTICK_DEVICE"));
envpath = envcopy;
while (envpath != NULL) {
delim = SDL_strchr(envpath, ':');
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);
}
}
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;
}
SDL_SYS_numjoysticks = numjoysticks;
return (numjoysticks);
}
int SDL_SYS_NumJoysticks()
{
return SDL_SYS_numjoysticks;
}
void SDL_SYS_JoystickDetect()
{
}
SDL_bool SDL_SYS_JoystickNeedsPolling()
{
return SDL_FALSE;
}
/* Function to get the device-dependent name of a joystick */
const char *
SDL_SYS_JoystickNameForDeviceIndex(int device_index)
{
int fd;
static char namebuf[128];
2012-11-26 21:11:28 -08:00
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;
}
/* 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;
}
static int
allocate_hatdata(SDL_Joystick * joystick)
{
int i;
joystick->hwdata->hats =
(struct hwdata_hat *) SDL_malloc(joystick->nhats *
sizeof(struct hwdata_hat));
if (joystick->hwdata->hats == NULL) {
return (-1);
}
for (i = 0; i < joystick->nhats; ++i) {
joystick->hwdata->hats[i].axis[0] = 1;
joystick->hwdata->hats[i].axis[1] = 1;
}
return (0);
}
static int
allocate_balldata(SDL_Joystick * joystick)
{
int i;
joystick->hwdata->balls =
(struct hwdata_ball *) SDL_malloc(joystick->nballs *
sizeof(struct hwdata_ball));
if (joystick->hwdata->balls == NULL) {
return (-1);
}
for (i = 0; i < joystick->nballs; ++i) {
joystick->hwdata->balls[i].axis[0] = 0;
joystick->hwdata->balls[i].axis[1] = 0;
}
return (0);
}
static void
ConfigJoystick(SDL_Joystick * joystick, int fd)
{
int i, t;
unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
unsigned long relbit[NBITS(REL_MAX)] = { 0 };
/* See if this device uses the new unified event API */
if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) &&
(ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0) &&
(ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) >= 0)) {
/* Get the number of buttons, axes, and other thingamajigs */
for (i = BTN_JOYSTICK; i < KEY_MAX; ++i) {
if (test_bit(i, keybit)) {
#ifdef DEBUG_INPUT_EVENTS
printf("Joystick has button: 0x%x\n", i);
#endif
joystick->hwdata->key_map[i - BTN_MISC] = joystick->nbuttons;
++joystick->nbuttons;
}
}
for (i = BTN_MISC; i < BTN_JOYSTICK; ++i) {
if (test_bit(i, keybit)) {
#ifdef DEBUG_INPUT_EVENTS
printf("Joystick has button: 0x%x\n", i);
#endif
joystick->hwdata->key_map[i - BTN_MISC] = joystick->nbuttons;
++joystick->nbuttons;
}
}
for (i = 0; i < ABS_MISC; ++i) {
/* Skip hats */
if (i == ABS_HAT0X) {
i = ABS_HAT3Y;
continue;
}
if (test_bit(i, absbit)) {
struct input_absinfo absinfo;
2011-01-24 15:10:16 -08:00
if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0)
continue;
#ifdef DEBUG_INPUT_EVENTS
printf("Joystick has absolute axis: %x\n", i);
printf("Values = { %d, %d, %d, %d, %d }\n",
absinfo.value, absinfo.minimum, absinfo.maximum,
absinfo.fuzz, absinfo.flat);
#endif /* DEBUG_INPUT_EVENTS */
joystick->hwdata->abs_map[i] = joystick->naxes;
if (absinfo.minimum == absinfo.maximum) {
joystick->hwdata->abs_correct[i].used = 0;
} else {
joystick->hwdata->abs_correct[i].used = 1;
joystick->hwdata->abs_correct[i].coef[0] =
(absinfo.maximum + absinfo.minimum) / 2 - absinfo.flat;
joystick->hwdata->abs_correct[i].coef[1] =
(absinfo.maximum + absinfo.minimum) / 2 + absinfo.flat;
t = ((absinfo.maximum - absinfo.minimum) / 2 - 2 * absinfo.flat);
if (t != 0) {
joystick->hwdata->abs_correct[i].coef[2] =
(1 << 29) / t;
} else {
joystick->hwdata->abs_correct[i].coef[2] = 0;
}
}
++joystick->naxes;
}
}
for (i = ABS_HAT0X; i <= ABS_HAT3Y; i += 2) {
if (test_bit(i, absbit) || test_bit(i + 1, absbit)) {
#ifdef DEBUG_INPUT_EVENTS
printf("Joystick has hat %d\n", (i - ABS_HAT0X) / 2);
#endif
++joystick->nhats;
}
}
if (test_bit(REL_X, relbit) || test_bit(REL_Y, relbit)) {
++joystick->nballs;
}
/* Allocate data to keep track of these thingamajigs */
if (joystick->nhats > 0) {
if (allocate_hatdata(joystick) < 0) {
joystick->nhats = 0;
}
}
if (joystick->nballs > 0) {
if (allocate_balldata(joystick) < 0) {
joystick->nballs = 0;
}
}
}
}
/* Function to open a joystick for use.
The joystick to open is specified by the index field of the joystick.
This should fill the nbuttons and naxes fields of the joystick structure.
It returns 0, or -1 if there is an error.
*/
int
SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
{
int fd;
char *fname;
/* 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);
}
joystick->instance_id = device_index;
joystick->hwdata = (struct joystick_hwdata *)
SDL_malloc(sizeof(*joystick->hwdata));
if (joystick->hwdata == NULL) {
SDL_OutOfMemory();
close(fd);
return (-1);
}
SDL_memset(joystick->hwdata, 0, sizeof(*joystick->hwdata));
joystick->hwdata->fd = fd;
joystick->hwdata->fname = fname;
/* Set the joystick to non-blocking read mode */
fcntl(fd, F_SETFL, O_NONBLOCK);
/* Get the number of buttons and axes on the joystick */
ConfigJoystick(joystick, fd);
return (0);
}
/* Function to determine is this joystick is attached to the system right now */
SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
{
return SDL_TRUE;
}
static __inline__ void
HandleHat(SDL_Joystick * stick, Uint8 hat, int axis, int value)
{
struct hwdata_hat *the_hat;
const Uint8 position_map[3][3] = {
{SDL_HAT_LEFTUP, SDL_HAT_UP, SDL_HAT_RIGHTUP},
{SDL_HAT_LEFT, SDL_HAT_CENTERED, SDL_HAT_RIGHT},
{SDL_HAT_LEFTDOWN, SDL_HAT_DOWN, SDL_HAT_RIGHTDOWN}
};
the_hat = &stick->hwdata->hats[hat];
if (value < 0) {
value = 0;
} else if (value == 0) {
value = 1;
} else if (value > 0) {
value = 2;
}
if (value != the_hat->axis[axis]) {
the_hat->axis[axis] = value;
SDL_PrivateJoystickHat(stick, hat,
position_map[the_hat->
axis[1]][the_hat->axis[0]]);
}
}
static __inline__ void
HandleBall(SDL_Joystick * stick, Uint8 ball, int axis, int value)
{
stick->hwdata->balls[ball].axis[axis] += value;
}
static __inline__ int
AxisCorrect(SDL_Joystick * joystick, int which, int value)
{
struct axis_correct *correct;
correct = &joystick->hwdata->abs_correct[which];
if (correct->used) {
if (value > correct->coef[0]) {
if (value < correct->coef[1]) {
return 0;
}
value -= correct->coef[1];
} else {
value -= correct->coef[0];
}
value *= correct->coef[2];
value >>= 14;
}
/* Clamp and return */
if (value < -32768)
return -32768;
if (value > 32767)
return 32767;
return value;
}
static __inline__ void
HandleInputEvents(SDL_Joystick * joystick)
{
struct input_event events[32];
int i, len;
int code;
while ((len = read(joystick->hwdata->fd, events, (sizeof events))) > 0) {
len /= sizeof(events[0]);
for (i = 0; i < len; ++i) {
code = events[i].code;
switch (events[i].type) {
case EV_KEY:
if (code >= BTN_MISC) {
code -= BTN_MISC;
SDL_PrivateJoystickButton(joystick,
joystick->hwdata->key_map[code],
events[i].value);
}
break;
case EV_ABS:
if (code >= ABS_MISC) {
break;
}
switch (code) {
case ABS_HAT0X:
case ABS_HAT0Y:
case ABS_HAT1X:
case ABS_HAT1Y:
case ABS_HAT2X:
case ABS_HAT2Y:
case ABS_HAT3X:
case ABS_HAT3Y:
code -= ABS_HAT0X;
HandleHat(joystick, code / 2, code % 2, events[i].value);
break;
default:
events[i].value =
AxisCorrect(joystick, code, events[i].value);
SDL_PrivateJoystickAxis(joystick,
joystick->hwdata->abs_map[code],
events[i].value);
break;
}
break;
case EV_REL:
switch (code) {
case REL_X:
case REL_Y:
code -= REL_X;
HandleBall(joystick, code / 2, code % 2, events[i].value);
break;
default:
break;
}
break;
default:
break;
}
}
}
}
void
SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
{
int i;
HandleInputEvents(joystick);
/* Deliver ball motion updates */
for (i = 0; i < joystick->nballs; ++i) {
int xrel, yrel;
xrel = joystick->hwdata->balls[i].axis[0];
yrel = joystick->hwdata->balls[i].axis[1];
if (xrel || yrel) {
joystick->hwdata->balls[i].axis[0] = 0;
joystick->hwdata->balls[i].axis[1] = 0;
SDL_PrivateJoystickBall(joystick, (Uint8) i, xrel, yrel);
}
}
}
/* Function to close a joystick after use */
void
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);
joystick->hwdata = NULL;
}
joystick->closed = 1;
}
/* Function to perform any system-specific joystick related cleanup */
void
SDL_SYS_JoystickQuit(void)
{
int i;
for (i = 0; SDL_joylist[i].fname; ++i) {
if (SDL_joylist[i].fname) {
SDL_free(SDL_joylist[i].fname);
SDL_joylist[i].fname = NULL;
}
}
}
JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
{
2012-11-26 21:11:28 -08:00
JoystickGUID guid;
// the GUID is just the first 16 chars of the name for now
const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index );
2012-11-26 21:11:28 -08:00
SDL_zero( guid );
SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
return guid;
}
JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
{
2012-11-26 21:11:28 -08:00
JoystickGUID guid;
// the GUID is just the first 16 chars of the name for now
2012-11-26 21:11:28 -08:00
const char *name = joystick->name;
SDL_zero( guid );
SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
return guid;
}
#endif /* SDL_JOYSTICK_LINUX */
2012-11-26 21:11:28 -08:00
/* vi: set ts=4 sw=4 expandtab: */