/*

joy.c - Linux Joystick

When compiling for Linux, we can use the Linux Joystick Driver, see
http://atrey.karlin.mff.cuni.cz/~vojtech/joystick/

*/

/*...sincludes:0:*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#ifndef NO_JOY
#include <linux/joystick.h>
#endif

#include "types.h"
#include "diag.h"
#include "common.h"
#include "kbd.h"
#include "joy.h"

/*...vtypes\46\h:0:*/
/*...vdiag\46\h:0:*/
/*...vcommon\46\h:0:*/
/*...vkbd\46\h:0:*/
/*...vjoy\46\h:0:*/
/*...e*/

/*...svars:0:*/
static int joy_emu = 0;
static const char *joy_buttons = "<LEFT><RIGHT><UP><DOWN><HOME> ";
static int joy_central = 0; /* Its as if Linux driver is already doing this */

#ifndef NO_JOY

static int joy_fd;
static int joy_left_row , joy_left_bitpos;
static int joy_right_row, joy_right_bitpos;
static int joy_up_row   , joy_up_bitpos;
static int joy_down_row , joy_down_bitpos;
#define	MAX_BUTTONS 16
static int joy_n_buttons;
static int joy_buttons_row[MAX_BUTTONS], joy_buttons_bitpos[MAX_BUTTONS];

#endif
/*...e*/

/*...sjoy_set_buttons:0:*/
void joy_set_buttons(const char *buttons)
	{
	joy_buttons = buttons;
	}
/*...e*/
/*...sjoy_set_central:0:*/
/* Expressed as a percentage */

void joy_set_central(int central)
	{
	joy_central = central * 32000 / 100;
	}
/*...e*/

/*...sjoy_periodic:0:*/
#ifndef NO_JOY
/*...sjoy_button:0:*/
static void joy_button(int row, int bitpos, BOOLEAN press)
	{
	if ( press )
		kbd_grid_press(row, bitpos);
	else
		kbd_grid_release(row, bitpos);
	}
/*...e*/
#endif

void joy_periodic(void)
	{
#ifndef NO_JOY
	if ( joy_emu & JOYEMU_JOY )
		{
		struct js_event ev;
		while ( read(joy_fd, &ev, sizeof(ev)) > 0 )
			{
			diag_message(DIAG_JOY_USAGE, "joystick event type=%d, number=%d, value=%d", ev.type, ev.number, ev.value);
			if ( ev.type & JS_EVENT_INIT )
				; /* Ignore this */
			else
				switch ( ev.type )
					{
					case JS_EVENT_AXIS:
						switch( ev.number )
							{
							case 0:
								joy_button(joy_left_row , joy_left_bitpos , ev.value < -joy_central);
								joy_button(joy_right_row, joy_right_bitpos, ev.value >  joy_central);
								break;
							case 1:
								joy_button(joy_up_row   , joy_up_bitpos   , ev.value < -joy_central);
								joy_button(joy_down_row , joy_down_bitpos , ev.value >  joy_central);
								break;
							}
						break;
					case JS_EVENT_BUTTON:
						if ( ev.number < joy_n_buttons )
							joy_button(joy_buttons_row[ev.number], joy_buttons_bitpos[ev.number], ev.value != 0);
						break;
					}
			}
		}
#endif
	}
/*...e*/

/*...sjoy_init:0:*/
void joy_init(int emu)
	{
#ifndef NO_JOY
	if ( emu & JOYEMU_JOY )
		{
		unsigned char axes;
		unsigned char buttons;
		char name[100];
		const char *p;
		BOOLEAN shifted;
		if ( (joy_fd = open("/dev/input/js0", O_RDONLY|O_NONBLOCK)) == -1 )
			fatal("can't open Linux joystick /dev/input/js0");
		ioctl(joy_fd, JSIOCGAXES, &axes);
		if ( axes < 2 )
			{
			close(joy_fd);
			fatal("joystick needs to support at least 2 axes");
			}
		ioctl(joy_fd, JSIOCGBUTTONS, &buttons);
		if ( buttons < 1 )
			{
			close(joy_fd);
			fatal("joystick needs to support at least 1 button");
			}
		ioctl(joy_fd, JSIOCGNAME(sizeof(name)), name);
		diag_message(DIAG_JOY_INIT, "found Linux \"%s\" joystick with %d axes and %d buttons", name, axes, buttons);
		p = joy_buttons;
		if ( kbd_find_grid(&p, &joy_left_row      , &joy_left_bitpos      , &shifted) &&
		     kbd_find_grid(&p, &joy_right_row     , &joy_right_bitpos     , &shifted) &&
		     kbd_find_grid(&p, &joy_up_row        , &joy_up_bitpos        , &shifted) &&
		     kbd_find_grid(&p, &joy_down_row      , &joy_down_bitpos      , &shifted) &&
		     kbd_find_grid(&p, &joy_buttons_row[0], &joy_buttons_bitpos[0], &shifted) )
			;
		else
			{
			close(joy_fd);
			fatal("can't parse joystick buttons: %s", joy_buttons);
			}
		joy_n_buttons = 1;
		while ( *p != '\0' && joy_n_buttons < MAX_BUTTONS && joy_n_buttons < buttons )
			{
			if ( ! kbd_find_grid(&p, &joy_buttons_row[joy_n_buttons], &joy_buttons_bitpos[joy_n_buttons], &shifted) )
				fatal("can't parse joystick extra buttons: \"%s\"", joy_buttons);
			++joy_n_buttons;
			}
		diag_message(DIAG_JOY_INIT, "%d axis central region, %d buttons configured to press keys", joy_central, joy_n_buttons);
		}
	joy_emu = emu;
#endif
	}
/*...e*/
/*...sjoy_term:0:*/
void joy_term(void)
	{
#ifndef NO_JOY
	if ( joy_emu & JOYEMU_JOY )
		close(joy_fd);
	joy_emu = 0;
#endif
	}
/*...e*/
