/*
gcc -DHAVE_CONFIG_H -I. -g -O2 -MD -MP -MF -c -o joy2key_joanillo joy2key_joanillo-0.0.5.c

Modificació joy2key (joanillo). És una modificació de joy2key per tal de solventar tot el que a mi no m'ha funcionat de joy2key. Tinc un joystick que és HID USB (V-USB). No he aconseguit fer funcionar algun dels exemples que es troben amb libusb, i per això opto per la solució de joy2key. Tanmateix, sendkeys és una mala solució i és millor implementar una solució que sigui independent de la finestra que selecciono, és a dir, una solució de baix nivell que treballi directament sobre el kernel. Això s'aconsegueix amb uinput.

joy2key_joanillo-0.0.2.c: començo a fer modificacions sobre joy2key.c. Detecta els botons i eixos i simplifico tot el que puc
joy2key_joanillo-0.0.3.c: implemento el autorepeat.
joy2key_joanillo-0.0.4.c: implemento el uinput.
joy2key_joanillo-0.0.5.c: fase final, neteja

This code is under the GNU Public License (see COPYING), you can do whatever you want to it, and if
you do anything really neat, I'd like to see it!

Based on joy2key (Peter Amstutz)

uinput: http://thiemonge.org/getting-started-with-uinput
$ sudo modprobe uinput
check: $ ls /dev/uinput
or
$ ls /dev/input/uinput
*/

#define DEFAULT_DEVICE		"/dev/input/js0"
#define XK_MISCELLANY		1
#define XK_LATIN1				1
#define NUM_BUTTONS			8
#define NUM_AXES				2
#define DEFAULT_AUTOREPEAT	30 //valors recomanats: de 5 a 50
#define WAIT_STEPS			20

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <linux/joystick.h>

#include <fcntl.h>
#include <errno.h>
#include <linux/input.h> //aquí és on estan definits els keycodes. ($ locate linux/input.h) /usr/src/linux-headers-3.2.0-23/include/linux/input.h
#include <linux/uinput.h>

char *device=DEFAULT_DEVICE;
int jsfd=-1;
char button_repeat_flags[NUM_BUTTONS];	//8 botons
char axis_hold_flags[NUM_AXES*2]; 		//2 eixos, que són 4 botons
int unsigned button_actions[NUM_BUTTONS]; //és el mapejat dels botons amb les tecles que s'ha de pitjar
int unsigned axis_actions[NUM_AXES][2];
struct itimerval repeat_time;

int check_device(int argc, char **argv);
void test_eixos();
void cleanup(int s);
void repeat_handler(int s);
int wait_button;
int wait_axis;

int fd;
struct uinput_user_dev uidev;
struct input_event ev;

#define die(str, args...) do { \
	perror(str); \
	exit(EXIT_FAILURE); \
} while(0)

int main(int argc, char **argv)
{
	puts("joy2key_joanillo - reads joystick status and dispatches keyboard events with uinput.");
	puts("By Joan Quintana (joanillo) (based on Peter Amstutz joy2key)");
	puts("This is free software under the GNU General Public License (GPL v2)");
	puts("(see COPYING in the joy2key archive)");
	puts("You are welcome to use/modify this code.");
	puts("Version 0.0.5\n");

	char string[255];
	char numaxes, numbuttons;

	struct js_event js;
	struct timeval tv;

	int res;

	//aquí va la info del programa, copyright i crèdits

	//***** MAPEJAT ***********
	button_actions[0] = KEY_1;
	button_actions[1] = KEY_2;
	button_actions[2] = KEY_3;
	button_actions[3] = KEY_4;
	button_actions[4] = KEY_5;
	button_actions[5] = KEY_6;
	button_actions[6] = KEY_7;
	button_actions[7] = KEY_8;

	axis_actions[0][0] = KEY_LEFT;
	axis_actions[0][1] = KEY_RIGHT;
	axis_actions[1][0] = KEY_UP;
	axis_actions[1][1] = KEY_DOWN;

	//**************************

	memset(button_repeat_flags, 0, sizeof(button_repeat_flags));
	memset(axis_hold_flags, 0, sizeof(axis_hold_flags));
	repeat_time.it_value.tv_sec=0;
	repeat_time.it_value.tv_usec=0;
	repeat_time.it_interval.tv_usec=1000000/DEFAULT_AUTOREPEAT;


	if((jsfd=open(device,O_RDONLY))==-1)
	{
		printf("Error opening %s!\n", device);
		puts("Are you sure you have joystick support in your kernel?");
		return 1;
	}

	if((jsfd=open(device,O_RDONLY))==-1)
	{
		printf("Error opening %s!\n", device);
		puts("Are you sure you have joystick support in your kernel?");
		return 1;
	}

	if (ioctl(jsfd, JSIOCGAXES, &numaxes)) {
	/* acording to the American Heritage Dictionary of the English Language 'axes' *IS* the correct pluralization of 'axis' */
		perror("joy2key_joanillo: error getting axes"); 
		return 1;
	}
	if (ioctl(jsfd, JSIOCGBUTTONS, &numbuttons)) {
		perror("joy2key_joanillo: error getting buttons");
		return 1;
	}

	//printf("%d\n", numaxes);
	//printf("%d\n", numbuttons);

	memset(&js, 0, sizeof(struct js_event)); //posem 0's

	signal(SIGINT, cleanup);
	signal(SIGTERM, cleanup);
	if(repeat_time.it_interval.tv_usec)
	{
		signal(SIGALRM, repeat_handler);
		repeat_time.it_value.tv_usec=repeat_time.it_interval.tv_usec;
		setitimer(ITIMER_REAL, &repeat_time, NULL);
	}

	// ** uinput ***
	//fd = open("/dev/input/uinput", O_WRONLY | O_NONBLOCK);
	fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
	if(fd < 0) die("error: open");

	if(ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0) die("error: ioctl");
	if(ioctl(fd, UI_SET_KEYBIT, BTN_LEFT) < 0) die("error: ioctl");

	if(ioctl(fd, UI_SET_EVBIT, EV_REL) < 0) die("error: ioctl");
	if(ioctl(fd, UI_SET_RELBIT, REL_X) < 0) die("error: ioctl");
	if(ioctl(fd, UI_SET_RELBIT, REL_Y) < 0) die("error: ioctl");

	if(ioctl(fd, UI_SET_KEYBIT, KEY_D) < 0) die("error: ioctl");

	ioctl(fd, UI_SET_KEYBIT, KEY_1);
	ioctl(fd, UI_SET_KEYBIT, KEY_2);
	ioctl(fd, UI_SET_KEYBIT, KEY_3);
	ioctl(fd, UI_SET_KEYBIT, KEY_4);
	ioctl(fd, UI_SET_KEYBIT, KEY_5);
	ioctl(fd, UI_SET_KEYBIT, KEY_6);
	ioctl(fd, UI_SET_KEYBIT, KEY_7);
	ioctl(fd, UI_SET_KEYBIT, KEY_8);
	ioctl(fd, UI_SET_KEYBIT, KEY_LEFT);
	ioctl(fd, UI_SET_KEYBIT, KEY_RIGHT);
	ioctl(fd, UI_SET_KEYBIT, KEY_UP);
	ioctl(fd, UI_SET_KEYBIT, KEY_DOWN);

	memset(&uidev, 0, sizeof(uidev));
	snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "uinput-sample");
	uidev.id.bustype = BUS_USB;
	uidev.id.vendor  = 0x1;
	uidev.id.product = 0x1;
	uidev.id.version = 1;

	if(write(fd, &uidev, sizeof(uidev)) < 0) die("error: write");

	if(ioctl(fd, UI_DEV_CREATE) < 0) die("error: ioctl");

	// ******************************

	/* Main Loop */
	for(;;)
	{
		memset(&js, 0, sizeof(struct js_event));
		res = read(jsfd, &js, sizeof(struct js_event));

		switch(js.type)
		{
		//printf("%d %d\n", js.number, js.value); //value: 1=press; 0=release
		//fflush(stdout);
		case JS_EVENT_BUTTON: /* buttons */
			if(button_actions[js.number])
			{
				button_repeat_flags[js.number]=js.value;
				//enviem la tecla				
				//printf("%d %d\n", js.number, js.value); //value: 1=press; 0=release
				if (js.value > 0) {
					//printf("%d %c\n", button_actions[js.number], button_actions[js.number]);
					memset(&ev, 0, sizeof(struct input_event));
					ev.type = EV_KEY;
					ev.code = button_actions[js.number];
					ev.value = 1;
					res = write(fd, &ev, sizeof(struct input_event));

					memset(&ev, 0, sizeof(struct input_event));
					ev.type = EV_KEY;
					ev.code = button_actions[js.number];
					ev.value = 0;
					res = write(fd, &ev, sizeof(struct input_event));

					memset(&ev, 0, sizeof(struct input_event));
					ev.type = EV_SYN;
					ev.code = 0;
					ev.value = 0;
					res = write(fd, &ev, sizeof(struct input_event));
				}

				if (js.value == 0) wait_button = 0; 
				//fflush(stdout);
			}
			break;
		case JS_EVENT_AXIS: /* axis */
			if(axis_actions[js.number][0] || axis_actions[js.number][1])
			{
				axis_hold_flags[js.number]=js.value;
				//enviem la tecla				
				//printf("%d %d\n", js.number, js.value); //value: 1=press; 0=release
				if (js.value < 0) {
					//printf("%d %c\n", axis_actions[js.number][0], axis_actions[js.number][0]);					
					memset(&ev, 0, sizeof(struct input_event));
					ev.type = EV_KEY;
					ev.code = axis_actions[js.number][0];
					ev.value = 1;
					res = write(fd, &ev, sizeof(struct input_event));

					memset(&ev, 0, sizeof(struct input_event));
					ev.type = EV_KEY;
					ev.code = axis_actions[js.number][0];
					ev.value = 0;
					res = write(fd, &ev, sizeof(struct input_event));

					memset(&ev, 0, sizeof(struct input_event));
					ev.type = EV_SYN;
					ev.code = 0;
					ev.value = 0;
					res = write(fd, &ev, sizeof(struct input_event));

				} else if (js.value > 0) {
					//printf("%d %c\n", axis_actions[js.number][1], axis_actions[js.number][1]);
				
					memset(&ev, 0, sizeof(struct input_event));
					ev.type = EV_KEY;
					ev.code = axis_actions[js.number][1];
					ev.value = 1;
					res = write(fd, &ev, sizeof(struct input_event));

					memset(&ev, 0, sizeof(struct input_event));
					ev.type = EV_KEY;
					ev.code = axis_actions[js.number][1];
					ev.value = 0;
					res = write(fd, &ev, sizeof(struct input_event));

					memset(&ev, 0, sizeof(struct input_event));
					ev.type = EV_SYN;
					ev.code = 0;
					ev.value = 0;
					res = write(fd, &ev, sizeof(struct input_event));

				} else {
					wait_axis = 0;
				}

				fflush(stdout);
			}
			break;
		} //switch

	} //main loop for
}

void cleanup(int s) //cal??
{
    printf("\n%s caught, cleaning up & quitting.\n", 
		   s==SIGINT ? "SIGINT" : 
		   (s==SIGTERM ? "SIGTERM" : ((s == 0) ? "Window die" : "Unknown")));
    exit(0);
}

//funció per gestionar quan deixo apretat el botó o l'eix
void repeat_handler(int s)
{
	int i;
	int res;
	signal(SIGALRM, SIG_IGN);

	for(i=0; i<NUM_BUTTONS; i++)
	{
		if(button_repeat_flags[i]) //detectem que la tecla encara està pitjada
		{
			if (wait_button > WAIT_STEPS) {
				if (button_repeat_flags[i] > 0) {
					//printf("%d %c\n", button_actions[i], button_actions[i]);
					memset(&ev, 0, sizeof(struct input_event));
					ev.type = EV_KEY;
					ev.code = button_actions[i];
					ev.value = 1;
					res = write(fd, &ev, sizeof(struct input_event));

					memset(&ev, 0, sizeof(struct input_event));
					ev.type = EV_KEY;
					ev.code = button_actions[i];
					ev.value = 0;
					res = write(fd, &ev, sizeof(struct input_event));

					memset(&ev, 0, sizeof(struct input_event));
					ev.type = EV_SYN;
					ev.code = 0;
					ev.value = 0;
					res = write(fd, &ev, sizeof(struct input_event));
				}
			} else {
				wait_button++;
			}
		}
	}

	for(i=0; i<NUM_AXES; i++)
	{
		if(axis_hold_flags[i]) //detectem que la tecla encara està pitjada
		{
			if (wait_axis > WAIT_STEPS) {
				if (axis_hold_flags[i] < 0) {
					//printf("%d %c\n", axis_actions[i][1], axis_actions[i][1]);

					memset(&ev, 0, sizeof(struct input_event));
					ev.type = EV_KEY;
					ev.code = axis_actions[i][1];
					ev.value = 1;
					res = write(fd, &ev, sizeof(struct input_event));

					memset(&ev, 0, sizeof(struct input_event));
					ev.type = EV_KEY;
					ev.code = axis_actions[i][1];
					ev.value = 0;
					res = write(fd, &ev, sizeof(struct input_event));

					memset(&ev, 0, sizeof(struct input_event));
					ev.type = EV_SYN;
					ev.code = 0;
					ev.value = 0;
					res = write(fd, &ev, sizeof(struct input_event));

				} else if (axis_hold_flags[i] > 0) {
					//printf("%d %c\n", axis_actions[i][0], axis_actions[i][0]);

					memset(&ev, 0, sizeof(struct input_event));
					ev.type = EV_KEY;
					ev.code = axis_actions[i][0];
					ev.value = 1;
					res = write(fd, &ev, sizeof(struct input_event));

					memset(&ev, 0, sizeof(struct input_event));
					ev.type = EV_KEY;
					ev.code = axis_actions[i][0];
					ev.value = 0;
					res = write(fd, &ev, sizeof(struct input_event));

					memset(&ev, 0, sizeof(struct input_event));
					ev.type = EV_SYN;
					ev.code = 0;
					ev.value = 0;
					res = write(fd, &ev, sizeof(struct input_event));

				}
			} else {
				wait_axis++;
			}
		}
	}
	signal(SIGALRM, repeat_handler);

}

