diff src/joystick/linux/SDL_sysjoystick.c @ 0:74212992fb08

Initial revision
author Sam Lantinga <slouken@lokigames.com>
date Thu, 26 Apr 2001 16:45:43 +0000
parents
children 0cc95f442f3a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/joystick/linux/SDL_sysjoystick.c	Thu Apr 26 16:45:43 2001 +0000
@@ -0,0 +1,742 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997, 1998, 1999, 2000, 2001  Sam Lantinga
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Sam Lantinga
+    slouken@devolution.com
+*/
+
+#ifdef SAVE_RCSID
+static char rcsid =
+ "@(#) $Id$";
+#endif
+
+/* This is the system specific header for the SDL joystick API */
+
+#include <stdio.h>		/* For the definition of NULL */
+#include <stdlib.h>		/* For getenv() prototype */
+#include <string.h>
+#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>
+#ifdef USE_INPUT_EVENTS
+#include <linux/input.h>
+#endif
+
+#include "SDL_error.h"
+#include "SDL_joystick.h"
+#include "SDL_sysjoystick.h"
+#include "SDL_joystick_c.h"
+
+/* Define this if you want to map axes to hats and trackballs */
+#define FANCY_HATS_AND_BALLS
+
+#ifdef FANCY_HATS_AND_BALLS
+/* Special joystick configurations:
+	'JoystickName' Naxes Nhats Nballs
+ */
+static const char *special_joysticks[] = {
+	"'MadCatz Panther XL' 3 2 1", /* We don't handle a rudder (axis 8) */
+	"'SideWinder Precision Pro' 4 1 0",
+	"'SideWinder 3D Pro' 4 1 0",
+	"'Microsoft SideWinder 3D Pro' 4 1 0",
+	"'Microsoft SideWinder Dual Strike USB version 1.0' 2 1 0",
+	"'WingMan Interceptor' 3 3 0",
+	/* WingMan Extreme Analog - not recognized by default
+	"'Analog 3-axis 4-button joystick' 2 1",
+	*/
+	"'WingMan Extreme Digital 3D' 4 1 0",
+	NULL
+};
+#else
+#undef USE_INPUT_EVENTS
+#endif
+
+/* The maximum number of joysticks we'll detect */
+#define MAX_JOYSTICKS	32
+
+/* A list of available joysticks */
+static char *SDL_joylist[MAX_JOYSTICKS];
+
+/* The private structure used to keep track of a joystick */
+struct joystick_hwdata {
+	int fd;
+	/* The current linux joystick driver maps hats to two axes */
+	int analog_hat;		/* Well, except for analog hats */
+	struct hwdata_hat {
+		int axis[2];
+	} *hats;
+	/* The current linux joystick driver maps balls to two axes */
+	struct hwdata_ball {
+		int axis[2];
+	} *balls;
+
+	/* Support for the Linux 2.4 unified input interface */
+	SDL_bool is_hid;
+#ifdef USE_INPUT_EVENTS
+	Uint8 key_map[KEY_MAX-BTN_MISC];
+	Uint8 abs_map[ABS_MAX];
+	struct axis_correct {
+		int used;
+		int coef[3];
+	} abs_correct[ABS_MAX];
+#endif
+};
+
+static char *mystrdup(const char *string)
+{
+	char *newstring;
+
+	newstring = (char *)malloc(strlen(string)+1);
+	if ( newstring ) {
+		strcpy(newstring, string);
+	}
+	return(newstring);
+}
+
+#ifdef USE_INPUT_EVENTS
+#define test_bit(nr, addr) \
+	(((1UL << ((nr) & 31)) & (((const unsigned int *) addr)[(nr) >> 5])) != 0)
+
+static int EV_IsJoystick(int fd)
+{
+	unsigned long evbit[40];
+	unsigned long keybit[40];
+	unsigned long absbit[40];
+
+	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) &&
+	     (test_bit(BTN_TRIGGER, keybit) || test_bit(BTN_A, keybit) || test_bit(BTN_1, keybit)))) return 0;
+	return(1);
+}
+
+#endif /* USE_INPUT_EVENTS */
+
+/* Function to scan the system for joysticks */
+int SDL_SYS_JoystickInit(void)
+{
+	/* The base path of the joystick devices */
+	const char *joydev_pattern[2] = {
+		"/dev/js%d",
+#ifdef USE_INPUT_EVENTS
+		"/dev/input/event%d"
+#else
+		"/dev/input/js%d"
+#endif
+	};
+	int numjoysticks;
+	int i, j, done;
+	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 a joystick to use */
+	if ( getenv("SDL_JOYSTICK_DEVICE") != NULL ) {
+		strncpy(path, getenv("SDL_JOYSTICK_DEVICE"), sizeof(path));
+		path[sizeof(path)-1] = '\0';
+		if ( stat(path, &sb) == 0 ) {
+			fd = open(path, O_RDONLY, 0);
+			if ( fd >= 0 ) {
+				/* Assume the user knows what they're doing. */
+				SDL_joylist[numjoysticks] = mystrdup(path);
+				if ( SDL_joylist[numjoysticks] ) {
+					dev_nums[numjoysticks] = sb.st_rdev;
+					++numjoysticks;
+				}
+				close(fd);
+			}
+		}
+	}
+	for ( i=0; i<SDL_TABLESIZE(joydev_pattern); ++i ) {
+		done = 0;
+		for ( j=0; (j < MAX_JOYSTICKS) && !done; ++j ) {
+			sprintf(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 USE_INPUT_EVENTS
+#ifdef DEBUG_INPUT_EVENTS
+				printf("Checking %s\n", path);
+#endif
+				if ( (i > 0) && ! EV_IsJoystick(fd) ) {
+					close(fd);
+					continue;
+				}
+#endif
+				close(fd);
+
+				/* We're fine, add this joystick */
+				SDL_joylist[numjoysticks] = mystrdup(path);
+				if ( SDL_joylist[numjoysticks] ) {
+					dev_nums[numjoysticks] = sb.st_rdev;
+					++numjoysticks;
+				}
+			} else {
+				done = 1;
+			}
+		}
+	}
+	return(numjoysticks);
+}
+
+/* Function to get the device-dependent name of a joystick */
+const char *SDL_SYS_JoystickName(int index)
+{
+	int fd;
+	static char namebuf[128];
+	char *name;
+
+	name = NULL;
+	fd = open(SDL_joylist[index], O_RDONLY, 0);
+	if ( fd >= 0 ) {
+		if ( 
+#ifdef USE_INPUT_EVENTS
+		     (ioctl(fd, EVIOCGNAME(sizeof(namebuf)), namebuf) <= 0) &&
+#endif
+		     (ioctl(fd, JSIOCGNAME(sizeof(namebuf)), namebuf) <= 0) ) {
+			name = SDL_joylist[index];
+		} else {
+			name = namebuf;
+		}
+		close(fd);
+	}
+	return name;
+}
+
+#ifdef FANCY_HATS_AND_BALLS
+
+static int allocate_hatdata(SDL_Joystick *joystick)
+{
+	int i;
+
+	joystick->hwdata->hats = (struct hwdata_hat *)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 *)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 SDL_bool ConfigJoystick(SDL_Joystick *joystick,
+			const char *name, const char *config)
+{
+	char cfg_name[128];
+	SDL_bool handled;
+
+	if ( config == NULL ) {
+		return(SDL_FALSE);
+	}
+	strcpy(cfg_name, "");
+	if ( *config == '\'' ) {
+		sscanf(config, "'%[^']s'", cfg_name);
+		config += strlen(cfg_name)+2;
+	} else {
+		sscanf(config, "%s", cfg_name);
+		config += strlen(cfg_name);
+	}
+	handled = SDL_FALSE;
+	if ( strcmp(cfg_name, name) == 0 ) {
+		/* Get the number of axes, hats and balls for this joystick */
+		int joystick_axes = joystick->naxes;
+		sscanf(config, "%d %d %d", 
+			&joystick->naxes, &joystick->nhats, &joystick->nballs);
+
+		/* Allocate the extra data for mapping them */
+		if ( joystick->nhats > 0 ) {
+			/* HACK: Analog hats map to only one axis */
+			if (joystick_axes == (joystick->naxes+joystick->nhats)){
+				joystick->hwdata->analog_hat = 1;
+			} else {
+				if ( allocate_hatdata(joystick) < 0 ) {
+					joystick->nhats = 0;
+				}
+				joystick->hwdata->analog_hat = 0;
+			}
+		}
+		if ( joystick->nballs > 0 ) {
+			if ( allocate_balldata(joystick) < 0 ) {
+				joystick->nballs = 0;
+			}
+		}
+		handled = SDL_TRUE;
+	}
+	return(handled);
+}
+
+#ifdef USE_INPUT_EVENTS
+
+static SDL_bool EV_ConfigJoystick(SDL_Joystick *joystick, int fd)
+{
+	int i;
+	unsigned long keybit[40];
+	unsigned long absbit[40];
+	unsigned long relbit[40];
+
+	/* 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) ) {
+		joystick->hwdata->is_hid = SDL_TRUE;
+
+		/* 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_MAX; ++i ) {
+			/* Skip hats */
+			if ( i == ABS_HAT0X ) {
+				i = ABS_HAT3Y;
+				continue;
+			}
+			if ( test_bit(i, absbit) ) {
+				int values[5];
+
+				ioctl(fd, EVIOCGABS(i), values);
+#ifdef DEBUG_INPUT_EVENTS
+				printf("Joystick has absolute axis: %x\n", i);
+				printf("Values = { %d, %d, %d, %d, %d }\n",
+					values[0], values[1],
+					values[2], values[3], values[4]);
+#endif /* DEBUG_INPUT_EVENTS */
+				joystick->hwdata->abs_map[i] = joystick->naxes;
+				if ( values[1] == values[2] ) {
+				    joystick->hwdata->abs_correct[i].used = 0;
+				} else {
+				    joystick->hwdata->abs_correct[i].used = 1;
+				    joystick->hwdata->abs_correct[i].coef[0] =
+					(values[2] + values[1]) / 2 - values[4];
+				    joystick->hwdata->abs_correct[i].coef[1] =
+					(values[2] + values[1]) / 2 + values[4];
+				    joystick->hwdata->abs_correct[i].coef[2] =
+					(1 << 29) / ((values[2] - values[1]) / 2 - 2 * values[4]);
+				}
+				++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;
+			}
+		}
+	}
+	return(joystick->hwdata->is_hid);
+}
+
+#endif /* USE_INPUT_EVENTS */
+
+#endif /* FANCY_HATS_AND_BALLS */
+
+/* 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)
+{
+#ifdef FANCY_HATS_AND_BALLS
+	const char *name;
+	int i;
+#endif
+	int fd;
+	unsigned char n;
+
+	/* Open the joystick and set the joystick file descriptor */
+	fd = open(SDL_joylist[joystick->index], O_RDONLY, 0);
+	if ( fd < 0 ) {
+		SDL_SetError("Unable to open %s\n",
+		             SDL_joylist[joystick->index]);
+		return(-1);
+	}
+	joystick->hwdata = (struct joystick_hwdata *)
+	                   malloc(sizeof(*joystick->hwdata));
+	if ( joystick->hwdata == NULL ) {
+		SDL_OutOfMemory();
+		close(fd);
+		return(-1);
+	}
+	memset(joystick->hwdata, 0, sizeof(*joystick->hwdata));
+	joystick->hwdata->fd = fd;
+
+	/* Set the joystick to non-blocking read mode */
+	fcntl(fd, F_SETFL, O_NONBLOCK);
+
+	/* Get the number of buttons and axes on the joystick */
+#ifdef USE_INPUT_EVENTS
+	if ( ! EV_ConfigJoystick(joystick, fd) )
+#endif
+	{
+		if ( ioctl(fd, JSIOCGAXES, &n) < 0 ) {
+			joystick->naxes = 2;
+		} else {
+			joystick->naxes = n;
+		}
+		if ( ioctl(fd, JSIOCGBUTTONS, &n) < 0 ) {
+			joystick->nbuttons = 2;
+		} else {
+			joystick->nbuttons = n;
+		}
+#ifdef FANCY_HATS_AND_BALLS
+		/* Check for special joystick support */
+		name = SDL_SYS_JoystickName(joystick->index);
+		for ( i=0; special_joysticks[i]; ++i ) {
+			if (ConfigJoystick(joystick,name,special_joysticks[i])){
+				break;
+			}
+		}
+		if ( special_joysticks[i] == NULL ) {
+			ConfigJoystick(joystick, name,
+					getenv("SDL_LINUX_JOYSTICK"));
+		}
+#endif /* FANCY_HATS_AND_BALLS */
+	}
+	return(0);
+}
+
+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]]);
+	}
+}
+
+/* This was necessary for the Wingman Extreme Analog joystick */
+static __inline__
+void HandleAnalogHat(SDL_Joystick *stick, Uint8 hat, int value)
+{
+	const Uint8 position_map[] = {
+		SDL_HAT_UP,
+		SDL_HAT_RIGHT,
+		SDL_HAT_DOWN,
+		SDL_HAT_LEFT,
+		SDL_HAT_CENTERED
+	};
+	SDL_PrivateJoystickHat(stick, hat, position_map[(value/16000)+2]);
+}
+
+static __inline__
+void HandleBall(SDL_Joystick *stick, Uint8 ball, int axis, int value)
+{
+	stick->hwdata->balls[ball].axis[axis] += value;
+}
+
+/* Function to update the state of a joystick - called as a device poll.
+ * This function shouldn't update the joystick structure directly,
+ * but instead should call SDL_PrivateJoystick*() to deliver events
+ * and update joystick device state.
+ */
+static __inline__ void JS_HandleEvents(SDL_Joystick *joystick)
+{
+	struct js_event events[32];
+	int i, len;
+	Uint8 other_axis;
+
+	while ((len=read(joystick->hwdata->fd, events, (sizeof events))) > 0) {
+		len /= sizeof(events[0]);
+		for ( i=0; i<len; ++i ) {
+			switch (events[i].type & ~JS_EVENT_INIT) {
+			    case JS_EVENT_AXIS:
+				if ( events[i].number < joystick->naxes ) {
+					SDL_PrivateJoystickAxis(joystick,
+				           events[i].number, events[i].value);
+					break;
+				}
+				events[i].number -= joystick->naxes;
+				if ( joystick->hwdata->analog_hat ) {
+					other_axis = events[i].number;
+					if ( other_axis < joystick->nhats ) {
+						HandleAnalogHat(joystick, other_axis,
+							events[i].value);
+						break;
+					}
+				} else {
+					other_axis = (events[i].number / 2);
+					if ( other_axis < joystick->nhats ) {
+						HandleHat(joystick, other_axis,
+							events[i].number%2,
+							events[i].value);
+						break;
+					}
+				}
+				events[i].number -= joystick->nhats*2;
+				other_axis = (events[i].number / 2);
+				if ( other_axis < joystick->nballs ) {
+					HandleBall(joystick, other_axis,
+						events[i].number%2,
+						events[i].value);
+					break;
+				}
+				break;
+			    case JS_EVENT_BUTTON:
+				SDL_PrivateJoystickButton(joystick,
+				           events[i].number, events[i].value);
+				break;
+			    default:
+				/* ?? */
+				break;
+			}
+		}
+	}
+}
+#ifdef USE_INPUT_EVENTS
+static __inline__ int EV_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 < -32767 ) {
+		value = -32767;
+	} else
+	if ( value > 32767 ) {
+		value = 32767;
+	}
+	return value;
+}
+
+static __inline__ void EV_HandleEvents(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:
+				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 = EV_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;
+			}
+		}
+	}
+}
+#endif /* USE_INPUT_EVENTS */
+
+void SDL_SYS_JoystickUpdate(SDL_Joystick *joystick)
+{
+	int i;
+	
+#ifdef USE_INPUT_EVENTS
+	if ( joystick->hwdata->is_hid )
+		EV_HandleEvents(joystick);
+	else
+#endif
+		JS_HandleEvents(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 ) {
+			free(joystick->hwdata->hats);
+		}
+		if ( joystick->hwdata->balls ) {
+			free(joystick->hwdata->balls);
+		}
+		free(joystick->hwdata);
+		joystick->hwdata = NULL;
+	}
+}
+
+/* Function to perform any system-specific joystick related cleanup */
+void SDL_SYS_JoystickQuit(void)
+{
+	int i;
+
+	for ( i=0; SDL_joylist[i]; ++i ) {
+		free(SDL_joylist[i]);
+	}
+	SDL_joylist[0] = NULL;
+}
+