I have a PS2->USB reduction that normally doesn't work properly (can't handle
L+R/U+D), however in the input kernel interface, it generates curious event in
case of L+R - the axis value is 128 instead of the usual 127. This patch makes
stepmania recognize that and work properly with this reduction.

The reduction vendor is Dragonplus (there isn't much more product
identification available), in case you are wondering. Maybe other reductions
work this way too.

If you have bought one already, try running input-events from input-utils
package first to check axis values generated by the pad.  Normally they should
be 127 (nothing pressed), 0 or 255. If 128 appears as well if both directions
are pressed, this patch should make stepmania work properly with your pad.

diff -ru StepMania-3.9-src/src/Makefile.am StepMania-3.9-src~/src/Makefile.am
--- StepMania-3.9-src/src/Makefile.am	2004-09-08 08:48:45.000000000 +0200
+++ StepMania-3.9-src~/src/Makefile.am	2007-07-04 19:14:06.000000000 +0200
@@ -135,6 +135,8 @@
 StepMania += SDL_utils.cpp SDL_utils.h
 endif
 
+InputHandler += arch/InputHandler/InputHandler_Linux_Joystick.cpp arch/InputHandler/InputHandler_Linux_Joystick.h
+
 # Platform-specific drivers:
 
 if UNIX
diff -ru StepMania-3.9-src/src/Makefile.in StepMania-3.9-src~/src/Makefile.in
--- StepMania-3.9-src/src/Makefile.in	2004-10-23 00:21:52.000000000 +0200
+++ StepMania-3.9-src~/src/Makefile.in	2007-07-04 19:18:21.000000000 +0200
@@ -45,7 +45,7 @@
 
 @HAVE_ALSA_TRUE@am__append_4 = $(ALSA_CFLAGS)
 @HAVE_FFMPEG_TRUE@am__append_5 = arch/MovieTexture/MovieTexture_FFMpeg.cpp arch/MovieTexture/MovieTexture_FFMpeg.h
-@HAVE_SDL_TRUE@am__append_6 = arch/InputHandler/InputHandler_SDL.cpp arch/InputHandler/InputHandler_SDL.h
+@HAVE_SDL_TRUE@am__append_6 = arch/InputHandler/InputHandler_SDL.cpp arch/InputHandler/InputHandler_SDL.h arch/InputHandler/InputHandler_Linux_Joystick.cpp arch/InputHandler/InputHandler_Linux_Joystick.h
 @HAVE_SDL_TRUE@am__append_7 = arch/LowLevelWindow/LowLevelWindow_SDL.cpp arch/LowLevelWindow/LowLevelWindow_SDL.h
 @HAVE_SDL_TRUE@am__append_8 = arch/LoadingWindow/LoadingWindow_SDL.cpp arch/LoadingWindow/LoadingWindow_SDL.h
 @HAVE_SDL_TRUE@am__append_9 = SDL_utils.cpp SDL_utils.h
@@ -646,6 +646,8 @@
 	arch/InputHandler/InputHandler.h \
 	arch/InputHandler/InputHandler_SDL.cpp \
 	arch/InputHandler/InputHandler_SDL.h \
+	arch/InputHandler/InputHandler_Linux_Joystick.cpp \
+	arch/InputHandler/InputHandler_Linux_Joystick.h \
 	arch/MovieTexture/MovieTexture.cpp \
 	arch/MovieTexture/MovieTexture.h \
 	arch/MovieTexture/MovieTexture_Null.cpp \
@@ -894,7 +896,7 @@
 	$(am__objects_10)
 @UNIX_TRUE@am__objects_12 = ArchHooks_Unix.$(OBJEXT)
 am__objects_13 = ArchHooks.$(OBJEXT) $(am__objects_12)
-@HAVE_SDL_TRUE@am__objects_14 = InputHandler_SDL.$(OBJEXT)
+@HAVE_SDL_TRUE@am__objects_14 = InputHandler_SDL.$(OBJEXT) InputHandler_Linux_Joystick.$(OBJEXT)
 am__objects_15 = InputHandler.$(OBJEXT) $(am__objects_14)
 @HAVE_FFMPEG_TRUE@am__objects_16 = MovieTexture_FFMpeg.$(OBJEXT)
 am__objects_17 = MovieTexture.$(OBJEXT) MovieTexture_Null.$(OBJEXT) \
@@ -1346,6 +1348,7 @@
 @AMDEP_TRUE@	./$(DEPDIR)/InputFilter.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/InputHandler.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/InputHandler_SDL.Po \
+@AMDEP_TRUE@	./$(DEPDIR)/InputHandler_Linux_Joystick.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/InputMapper.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/InputQueue.Po ./$(DEPDIR)/Inventory.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/Judgment.Po ./$(DEPDIR)/JukeboxMenu.Po \
@@ -1783,6 +1786,7 @@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/InputFilter.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/InputHandler.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/InputHandler_SDL.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/InputHandler_Linux_Joystick.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/InputMapper.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/InputQueue.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Inventory.Po@am__quote@
@@ -2518,6 +2522,28 @@
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o InputHandler_SDL.obj `if test -f 'arch/InputHandler/InputHandler_SDL.cpp'; then $(CYGPATH_W) 'arch/InputHandler/InputHandler_SDL.cpp'; else $(CYGPATH_W) '$(srcdir)/arch/InputHandler/InputHandler_SDL.cpp'; fi`
 
+InputHandler_Linux_Joystick.o: arch/InputHandler/InputHandler_Linux_Joystick.cpp
+@am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT InputHandler_Linux_Joystick.o -MD -MP -MF "$(DEPDIR)/InputHandler_Linux_Joystick.Tpo" \
+@am__fastdepCXX_TRUE@	  -c -o InputHandler_Linux_Joystick.o `test -f 'arch/InputHandler/InputHandler_Linux_Joystick.cpp' || echo '$(srcdir)/'`arch/InputHandler/InputHandler_Linux_Joystick.cpp; \
+@am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/InputHandler_Linux_Joystick.Tpo" "$(DEPDIR)/InputHandler_Linux_Joystick.Po"; \
+@am__fastdepCXX_TRUE@	else rm -f "$(DEPDIR)/InputHandler_Linux_Joystick.Tpo"; exit 1; \
+@am__fastdepCXX_TRUE@	fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='arch/InputHandler/InputHandler_Linux_Joystick.cpp' object='InputHandler_Linux_Joystick.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	depfile='$(DEPDIR)/InputHandler_Linux_Joystick.Po' tmpdepfile='$(DEPDIR)/InputHandler_Linux_Joystick.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o InputHandler_Linux_Joystick.o `test -f 'arch/InputHandler/InputHandler_Linux_Joystick.cpp' || echo '$(srcdir)/'`arch/InputHandler/InputHandler_Linux_Joystick.cpp
+
+InputHandler_Linux_Joystick.obj: arch/InputHandler/InputHandler_Linux_Joystick.cpp
+@am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT InputHandler_Linux_Joystick.obj -MD -MP -MF "$(DEPDIR)/InputHandler_Linux_Joystick.Tpo" \
+@am__fastdepCXX_TRUE@	  -c -o InputHandler_Linux_Joystick.obj `if test -f 'arch/InputHandler/InputHandler_Linux_Joystick.cpp'; then $(CYGPATH_W) 'arch/InputHandler/InputHandler_Linux_Joystick.cpp'; else $(CYGPATH_W) '$(srcdir)/arch/InputHandler/InputHandler_Linux_Joystick.cpp'; fi`; \
+@am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/InputHandler_Linux_Joystick.Tpo" "$(DEPDIR)/InputHandler_Linux_Joystick.Po"; \
+@am__fastdepCXX_TRUE@	else rm -f "$(DEPDIR)/InputHandler_Linux_Joystick.Tpo"; exit 1; \
+@am__fastdepCXX_TRUE@	fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='arch/InputHandler/InputHandler_Linux_Joystick.cpp' object='InputHandler_Linux_Joystick.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	depfile='$(DEPDIR)/InputHandler_Linux_Joystick.Po' tmpdepfile='$(DEPDIR)/InputHandler_Linux_Joystick.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o InputHandler_Linux_Joystick.obj `if test -f 'arch/InputHandler/InputHandler_Linux_Joystick.cpp'; then $(CYGPATH_W) 'arch/InputHandler/InputHandler_Linux_Joystick.cpp'; else $(CYGPATH_W) '$(srcdir)/arch/InputHandler/InputHandler_Linux_Joystick.cpp'; fi`
+
 MovieTexture.o: arch/MovieTexture/MovieTexture.cpp
 @am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT MovieTexture.o -MD -MP -MF "$(DEPDIR)/MovieTexture.Tpo" \
 @am__fastdepCXX_TRUE@	  -c -o MovieTexture.o `test -f 'arch/MovieTexture/MovieTexture.cpp' || echo '$(srcdir)/'`arch/MovieTexture/MovieTexture.cpp; \
diff -ru StepMania-3.9-src/src/ScreenGameplay.cpp StepMania-3.9-src~/src/ScreenGameplay.cpp
--- StepMania-3.9-src/src/ScreenGameplay.cpp	2005-05-10 23:39:09.000000000 +0200
+++ StepMania-3.9-src~/src/ScreenGameplay.cpp	2007-07-05 03:34:51.000000000 +0200
@@ -798,6 +798,8 @@
 
 bool ScreenGameplay::IsLastSong()
 {
+	if( !GAMESTATE->IsCourseMode() )
+		return true;
 	if( GAMESTATE->m_pCurCourse  &&  GAMESTATE->m_pCurCourse->m_bRepeat )
 		return false;
 	return GAMESTATE->GetCourseSongIndex()+1 == (int)m_apSongsQueue.size(); // GetCourseSongIndex() is 0-based but size() is not
@@ -1626,6 +1628,7 @@
 void ScreenGameplay::Input( const DeviceInput& DeviceI, const InputEventType type, const GameInput &GameI, const MenuInput &MenuI, const StyleInput &StyleI )
 {
 	//LOG->Trace( "ScreenGameplay::Input()" );
+	//
 
 	if( type == IET_LEVEL_CHANGED )
 		return;
@@ -1814,6 +1817,7 @@
 		}
 	}
 
+
 	//
 	// handle a step or battle item activate
 	//
diff -ru StepMania-3.9-src/src/arch/InputHandler/InputHandler_Linux_Joystick.cpp StepMania-3.9-src~/src/arch/InputHandler/InputHandler_Linux_Joystick.cpp
--- StepMania-3.9-src/src/arch/InputHandler/InputHandler_Linux_Joystick.cpp	2004-05-15 23:59:40.000000000 +0200
+++ StepMania-3.9-src~/src/arch/InputHandler/InputHandler_Linux_Joystick.cpp	2007-07-05 12:23:12.000000000 +0200
@@ -9,54 +9,67 @@
 
 #include <errno.h>
 #include <sys/types.h>
-#include <linux/joystick.h>
+#include <linux/input.h>
 
 #include <set>
 
-static const char *Paths[InputHandler_Linux_Joystick::NUM_JOYSTICKS] =
-{
-	"/dev/js0",
-	"/dev/js1",
-	"/dev/input/js0",
-	"/dev/input/js1",
-};
-
 InputHandler_Linux_Joystick::InputHandler_Linux_Joystick()
 {
 	for(int i = 0; i < NUM_JOYSTICKS; ++i)
 		fds[i] = -1;
 
-	/* We check both eg. /dev/js0 and /dev/input/js0.  If both exist, they're probably
-	 * the same device; keep track of device IDs so we don't open the same joystick
-	 * twice. */
-	set< pair<int,int> > devices;
-	
-	for(int i = 0; i < NUM_JOYSTICKS; ++i)
-	{
-		struct stat st;
-		if( stat( Paths[i], &st ) == -1 )
-		{
-			if( errno != ENOENT )
-				LOG->Warn( "Couldn't stat %s: %s", Paths[i], strerror(errno) );
-			continue;
-		}
-
-		if( !S_ISCHR( st.st_mode ) )
-		{
-			LOG->Warn( "Ignoring %s: not a character device", Paths[i] );
-			continue;
-		}
-
-		pair<int,int> dev( major(st.st_rdev), minor(st.st_rdev) );
-		if( devices.find(dev) != devices.end() )
-			continue; /* dupe */
-		devices.insert( dev );
+	FILE *f = fopen("/proc/bus/input/devices", "r");
+	if (!f) {
+		LOG->Warn("Cannot open /proc/bus/input/devices: %s", strerror(errno));
+		return;
+	}
 
-		fds[i] = open( Paths[i], O_RDONLY );
+	char line[255];
+	int i = 0;
 
-		if(fds[i] != -1)
-			LOG->Info("Opened %s", Paths[i]);
+	// extra-hackish
+	while (fgets(line, 255, f)) {
+		if (line[0] == 'H') {
+			fprintf(stderr, "?%s\n", line);
+			// H: Handlers=js0 event3
+			char *hlist = strchr(line, '=');
+			if (!hlist)
+				continue;
+			hlist++;
+			fprintf(stderr, "+%s\n", hlist);
+			char *tok = strtok(hlist, " ");
+			char *evdev = NULL;
+			bool interesting = 0;
+			do {
+				fprintf(stderr, "??%s\n", tok);
+				if (!strncmp(tok, "event", 5)) {
+					fprintf(stderr, "e\n");
+					evdev = tok;
+					continue;
+				}
+				if (!strncmp(tok, "js", 2)) {
+					fprintf(stderr, "i\n");
+					interesting = 1;
+					continue;
+				}
+			} while ((tok = strtok(NULL, " ")));
+			if (interesting && evdev) {
+				fprintf(stderr, ">%s\n", evdev);
+				strncpy(evdev - 11, "/dev/input/", 11);
+				LOG->Trace("Trying %s", evdev - 11);
+				fds[i] = open( evdev - 11, O_RDONLY );
+
+				if(fds[i] == -1)
+					LOG->Warn("Cannot open %s: %s", evdev - 11, strerror(errno));
+				LOG->Info("Opened %s", evdev - 11);
+				if (++i >= NUM_JOYSTICKS)
+					break;
+			}
+		}
 	}
+
+	LOG->Info("Found %d joysticks", i);
+	fclose(f);
 }
 	
 InputHandler_Linux_Joystick::~InputHandler_Linux_Joystick()
@@ -97,7 +110,7 @@
 			if(!FD_ISSET(fds[i], &fdset))
 				continue;
 
-			js_event event;
+			input_event event;
 			int ret = read(fds[i], &event, sizeof(event));
 			if(ret != sizeof(event))
 			{
@@ -107,27 +120,34 @@
 				continue;
 			}
 
+			//fprintf(stderr, "joy ev c:%x ty:%x v:%x\n", event.code, event.type, event.value);
+
 			InputDevice id = InputDevice(DEVICE_JOY1 + i);
 
-			event.type &= ~JS_EVENT_INIT;
 			switch (event.type) {
-			case JS_EVENT_BUTTON: {
-				ButtonPressed(DeviceInput(id, JOY_1 + event.number), event.value);
+			case EV_KEY: {
+				ButtonPressed(DeviceInput(id, JOY_1 + (event.code - BTN_JOYSTICK)), event.value);
 				break;
 			}
 				
-			case JS_EVENT_AXIS: {
-				JoystickButton neg = (JoystickButton)(JOY_LEFT+2*event.number);
-				JoystickButton pos = (JoystickButton)(JOY_RIGHT+2*event.number);
-				ButtonPressed(DeviceInput(id, neg), event.value < -16000);
-				ButtonPressed(DeviceInput(id, pos), event.value > +16000);
+			case EV_ABS: {
+				JoystickButton neg = (JoystickButton)(JOY_LEFT+2*event.code);
+				JoystickButton pos = (JoystickButton)(JOY_RIGHT+2*event.code);
+				//fprintf(stderr, "<JOY neg: %d\n", event.value < 127 || event.value == 128);
+				//fprintf(stderr, "<JOY pos: %d\n", event.value > 129 || event.value == 128);
+				// I'm puzzled about the RageTimer() thing, but it took me a good one hour
+				// to debug and I'm just happy that it's working. :-)
+				// Maybe we could make use of the timeval we got from the event stream,
+				// if you are a sync freak.
+				ButtonPressed(DeviceInput(id, neg, 1, RageTimer()), event.value < 127 || event.value == 128);
+				ButtonPressed(DeviceInput(id, pos, 1, RageTimer()), event.value > 129 || event.value == 128);
 				break;
 			}
 				
 			default:
-				LOG->Warn("Unexpected packet (type %i) from joystick %i; disabled", event.type, i);
-				close(fds[i]);
-				fds[i] = -1;
+				LOG->Warn("Unexpected packet (type %i) from joystick %i", event.type, i);
+				//close(fds[i]);
+				///fds[i] = -1;
 				continue;
 			}
 
diff -ru StepMania-3.9-src/src/arch/InputHandler/InputHandler_Linux_Joystick.h StepMania-3.9-src~/src/arch/InputHandler/InputHandler_Linux_Joystick.h
--- StepMania-3.9-src/src/arch/InputHandler/InputHandler_Linux_Joystick.h	2004-05-15 23:59:40.000000000 +0200
+++ StepMania-3.9-src~/src/arch/InputHandler/InputHandler_Linux_Joystick.h	2007-07-04 22:08:08.000000000 +0200
@@ -7,7 +7,7 @@
 class InputHandler_Linux_Joystick: public InputHandler
 {
 public:
-	enum { NUM_JOYSTICKS = 4 };
+	enum { NUM_JOYSTICKS = 2 };
 	void Update(float fDeltaTime);
 	InputHandler_Linux_Joystick();
 	~InputHandler_Linux_Joystick();
diff -ru StepMania-3.9-src/src/arch/InputHandler/InputHandler_SDL.cpp StepMania-3.9-src~/src/arch/InputHandler/InputHandler_SDL.cpp
--- StepMania-3.9-src/src/arch/InputHandler/InputHandler_SDL.cpp	2007-07-04 22:06:30.000000000 +0200
+++ StepMania-3.9-src~/src/arch/InputHandler/InputHandler_SDL.cpp	2007-07-04 19:10:44.000000000 +0200
@@ -144,7 +144,8 @@
 	//
 	// Init joysticks
 	//
-	int iNumJoySticks = min( SDL_NumJoysticks(), NUM_JOYSTICKS );
+	//int iNumJoySticks = min( SDL_NumJoysticks(), NUM_JOYSTICKS );
+	int iNumJoySticks = 0;
 	LOG->Info( "Found %d joysticks", iNumJoySticks );
 	int i;
 	for( i=0; i<iNumJoySticks; i++ )
@@ -226,6 +227,7 @@
 			InputDevice i = InputDevice(DEVICE_JOY1 + event.jaxis.which);
 			JoystickButton neg = (JoystickButton)(JOY_LEFT+2*event.jaxis.axis);
 			JoystickButton pos = (JoystickButton)(JOY_RIGHT+2*event.jaxis.axis);
+			fprintf (stderr, "jaxis %d\n", event.jaxis.value);
 			float l = SCALE( event.jaxis.value, 0.0f, 32768.0f, 0.0f, 1.0f );
 			ButtonPressed(DeviceInput(i, neg,max(-l,0),RageZeroTimer), event.jaxis.value < -16000);
 			ButtonPressed(DeviceInput(i, pos,max(+l,0),RageZeroTimer), event.jaxis.value > +16000);
@@ -253,7 +255,7 @@
 	vDevicesOut.push_back( DEVICE_KEYBOARD );
 	vDescriptionsOut.push_back( "Keyboard" );
 
-	for( int i=0; i<SDL_NumJoysticks(); i++ )
+	for( int i=0; i<0/*SDL_NumJoysticks()*/; i++ )
 	{
 		if( SDL_JoystickOpened(i) )
 		{
diff -ru StepMania-3.9-src/src/arch/arch.cpp StepMania-3.9-src~/src/arch/arch.cpp
--- StepMania-3.9-src/src/arch/arch.cpp	2004-07-07 22:11:29.000000000 +0200
+++ StepMania-3.9-src~/src/arch/arch.cpp	2007-07-04 19:11:53.000000000 +0200
@@ -74,6 +74,7 @@
 #if defined(SUPPORT_SDL_INPUT)
 	Add.push_back(new InputHandler_SDL);
 #endif
+	Add.push_back(new InputHandler_Linux_Joystick);
 }
 
 RageSoundDriver *MakeRageSoundDriver(CString drivers)
diff -ru StepMania-3.9-src/src/arch/arch_default.h StepMania-3.9-src~/src/arch/arch_default.h
--- StepMania-3.9-src/src/arch/arch_default.h	2004-06-11 03:08:09.000000000 +0200
+++ StepMania-3.9-src~/src/arch/arch_default.h	2007-07-04 19:09:42.000000000 +0200
@@ -27,6 +27,8 @@
 #define SUPPORT_SDL_INPUT
 #include "InputHandler/InputHandler_SDL.h"
 
+#include "InputHandler/InputHandler_Linux_Joystick.h"
+
 /* Load default fallback drivers; some of these may be overridden by arch-specific
  * drivers.  These are all singleton drivers--we never use more than one. */
 #include "LoadingWindow/LoadingWindow_Null.h"
