/*****************************************************************************
 * $Id: joystick_linux.cc,v 1.32 2006/07/23 02:09:11 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Joystick functions (Linux).  This is actually a header file.
 * @remarks Documented at joystick.hh
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#include "joystick.hh"

namespace gfxInput {

/// Axis numbers from Linux joystick driver event.
enum
{
	eLinuxJoyAxis_LEFTRIGHT	= 0,
	eLinuxJoyAxis_UPDOWN	= 1,
	eLinuxJoyAxis_THIRD		= 2,
};

static const char	sStrJoyDev[]	= "/dev/input/js0";
static const char	sStrJoyDevAlt[]	= "/dev/js0";
static int			sFiledescJoy	= -1;

/*****************************************************************************
 * Open/close joystick device.
 *****************************************************************************/
bool
Joystick::Open( void )
{
	// Open the joystick device only once.  Repetively opening-and-closing
	// it causes superfluous events and unreliable joystick readings.
	if ( sFiledescJoy != -1 ) return true;

	// Open in non-blocking mode.
	sFiledescJoy = open( sStrJoyDev, O_RDONLY|O_NONBLOCK );
	if ( sFiledescJoy == -1 )
	{
		// Try alternate/old device file.
		sFiledescJoy = open( sStrJoyDevAlt, O_RDONLY|O_NONBLOCK );
	}
	return sFiledescJoy != -1;
}
void
Joystick::Close( void )
{
	if ( sFiledescJoy != -1 ) close( sFiledescJoy );
	sFiledescJoy = -1;
}

/*****************************************************************************
 * Poll the next joystick event (non-blocking).
 * @return True if an event(s) occurred.
 * Updates the .mEvent member struct.
 *****************************************************************************/
bool
Joystick::PollEvent( void )
{
	// Forget previous event.
	ClearEvent();

	// Is joystick enabled?
	if ( ! mEnabled || sFiledescJoy == -1 ) return false;

	// Read several times from the Linux joystick driver.
	// Use the latest event: toss any older queued events in order to
	// avoid the annoyance of a busy program processing from an event queue
	// after the user has stopping moving the joystick.
	// Button events have priority (break immediately when one is read).
	// One axis and/or one button event can be reported.
	for ( int i = 0; i < 10; ++i )
	{
		// Read the next event from the Linux joystick driver.
		struct js_event eventLinux;
		int bytesRead = read( sFiledescJoy, &eventLinux, sizeof(struct js_event) );
		if ( bytesRead != sizeof(struct js_event) )
			break;

		// Button event?
		if ( eventLinux.type == JS_EVENT_BUTTON )
		{
			mEvent.mButton    = (eJoyButton) (eventLinux.number + 1); // which button
			mEvent.mButtonVal = eventLinux.value;  // bool: pressed/released
			break; // break immediately to report button event (button has priority)
		}

		// An axis event?
		if ( eventLinux.type == JS_EVENT_AXIS )
		{
			switch ( eventLinux.number )
			{
				// Left/right axis event?
				case eLinuxJoyAxis_LEFTRIGHT:
				mEvent.mAxis    = eJoyAxis_LEFTRIGHT;
				mEvent.mAxisVal = eventLinux.value * mAxisDir[0]; // if reversed axis
				break;

				// Up/down axis event?
				case eLinuxJoyAxis_UPDOWN:
				mEvent.mAxis    = eJoyAxis_UPDOWN;
				mEvent.mAxisVal = eventLinux.value * mAxisDir[1]; // if reversed axis
				break;

				// Third axis event?
				case eLinuxJoyAxis_THIRD:
				mEvent.mAxis    = eJoyAxis_THIRD;
				mEvent.mAxisVal = eventLinux.value * mAxisDir[2]; // if reversed axis
				break;

				default: break;
			}
		}
	}

	// Return true if any event occured.
	return    mEvent.mAxis   != eJoyAxis_NONE
           || mEvent.mButton != eJoyButton_NONE;
}

} // namespace gfxInput
