/*
 * Xpsitouch handles the Psion LCD toolbars.
 * 
 * Slightly influenced by xbut and XRmouse, thanks!
 *
 * Copyright 2002 Tony Lindgren <tony@atomide.com>
 * License:	GPL 
 * Warranty:	None
 */

#include "xpsitouch.h"

touchpanel *tp;

int main(int argc, char *argv[])
{
	XEvent xev;
	
	tp = (touchpanel *)tp_init(argc, argv);

	for(;;) {
		XNextEvent(tp->display, &xev);
		switch (xev.type) {
		case EnterNotify:
			TPDEBUG("EnterNotify: x = %d y = %d\n", 
				xev.xmotion.x, xev.xmotion.y);
			tp_handle_toolbars(tp, xev);
			break;
		case MotionNotify:
			TPDEBUG("MotionNotify: x = %d y = %d\n", 
				xev.xmotion.x, xev.xmotion.y);
			tp_handle_toolbars(tp, xev);
			break;
		case KeyPress:
			tp_handle_keypress(tp, xev);
			break;
		case KeyRelease:
			tp_handle_keyrelease(tp, xev);
			break;
		default:
			TPDEBUG("Unhandled event: %d\n", xev.type);
			break;
		}		
	}

	tp_exit(tp, 1);
}

void tp_exec_program(touchpanel *tp, int toolbar, int button)
{
	char *path = TOOLBAR_SYSTEM_SHELL_SCRIPT;
	int pid, ret = 0;
	char *argv[4];
	char arg1[4];
	char arg2[4];

	(void)signal(SIGCLD, SIG_IGN);
	pid = fork();
	if (pid < 0) {
		printf("Could not fork\n");
	} else if (pid == 0) {
		argv[0] = "sh";
		argv[1] = arg1;
		argv[2] = arg2;
		argv[3] = NULL;
		sprintf(argv[1], "%d", toolbar);
		sprintf(argv[2], "%d", button);
		ret = execvp(path, argv);
		exit(111);
	} else {
		/* Parent */
	}
}

void tp_handle_zoom_buttons(touchpanel *tp, XEvent xev)
{
	int zoom_button;
	
	/* Divide the 2 button zoom control area into 3 buttons */
	zoom_button = ((xev.xmotion.y - (3 * Y_TOOLB_BUTTON_HEIGHT)) 
		       / (2 * Y_TOOLB_BUTTON_HEIGHT / 3)) + 1;
	TPDEBUG("zoom_button: %d\n", zoom_button);

	if (tp->queued) {
		TPDEBUG("Already queued, ignoring press\n");
		return;
	}

	tp->x_no_buttons = XGetPointerMapping(tp->display, 
					      tp->orig_map, 
					      NUM_MAX_MOUSEBUTTONS);
	tp->queued = 1;
	tp->new_map[0] = (unsigned char)zoom_button;
	while (XSetPointerMapping(tp->display, 
				  tp->new_map, 
				  tp->x_no_buttons) == MappingBusy) {};


	switch (zoom_button) {
	case 1:
		break;
	case 2:
	case 3:
#ifdef AUTO_RESTORE
		usleep(AUTO_RESTORE_VAL);
		break;
#endif
	}

	while (XSetPointerMapping(tp->display, 
				  tp->orig_map, 
				  tp->x_no_buttons) == MappingBusy) {};
	tp->queued = 0;
}

void tp_handle_toolbars(touchpanel *tp, XEvent xev)
{
	int button;

	/* We don't care about coordinates outside the toolbar */
	if ((xev.xmotion.y < X_TOOLB_Y_TRIGGER) &&
	    (xev.xmotion.x > Y_TOOLB_X_TRIGGER)) {
		return;
	}

	/* Handle bottom "X" LCD toolbar */
	if (xev.xmotion.y >= X_TOOLB_Y_TRIGGER) {
		button = ((xev.xmotion.x + X_TOOLB_CALIBRATION) 
			  / X_TOOLB_BUTTON_WIDTH) + 1;
		tp_exec_program(tp, 1, button);
		return;
	}

	/* Handle left "Y" LCD toolbar */
	if (xev.xmotion.x <= Y_TOOLB_X_TRIGGER) {
		button = ((xev.xmotion.y + Y_TOOLB_CALIBRATION) 
			  / Y_TOOLB_BUTTON_HEIGHT) + 1;

		/* 
		 * We use the zoom controller for toggling the 
		 * pointer buttons.
		 */
		if (button >= 4) {
			tp_handle_zoom_buttons(tp, xev);
			return;
		}

		tp_exec_program(tp, 2, button);
		return;
	}
}

void tp_handle_keypress(touchpanel *tp, XEvent xev)
{
	int i, j, button;
	
	if (tp->queued) {
		TPDEBUG("Already queued, ignoring press\n");
		return;
	}

	tp->queued = 1;

	for (i = 0; i < tp->no_keys; i++) {
		if (xev.xkey.keycode == tp->keymap_list[i]->keycode) {

			/* Button is always the index + 1 */
			button = i + 1;

			tp->x_no_buttons = 
				XGetPointerMapping(tp->display, 
						   tp->orig_map, 
						   NUM_MAX_MOUSEBUTTONS);

#ifdef DEBUG
			printf("Saved to tp->orig_map: ");
			for (j = 0; j < NUM_MAX_MOUSEBUTTONS; j++) {
				printf("%d ", tp->orig_map[j]);
			}
			printf("\n");
#endif

			tp->new_map[0] = (unsigned char)button;

#ifdef DEBUG
			printf("Setting tp->new_map: ");
			for (j = 0; j < NUM_MAX_MOUSEBUTTONS; j++) {
				printf("%d ", tp->new_map[j]);
			}
			printf("\n");
#endif

			while (XSetPointerMapping(tp->display, 
						  tp->new_map, 
						  tp->x_no_buttons) 
			       == MappingBusy) {};

		}
	}
}

void tp_handle_keyrelease(touchpanel *tp, XEvent xev)
{
	int j;

#ifdef DEBUG
	printf("Restoring tp->orig_map: ");
	for (j = 0; j < NUM_MAX_MOUSEBUTTONS; j++) {
		printf("%d ", tp->orig_map[j]);
	}
	printf("\n");
#endif

#ifdef AUTO_RESTORE
	usleep(AUTO_RESTORE_VAL);
#endif

	while (XSetPointerMapping(tp->display, 
				  tp->orig_map, 
				  tp->x_no_buttons) 
	       == MappingBusy) {};
	tp->queued = 0;
}

touch_keymap* tp_new_keymap()
{
	touch_keymap *keymap = NULL;

	keymap = (touch_keymap *)malloc(sizeof(touch_keymap));
	if (keymap == NULL) {
		printf("Could not allocate memory for a new keymap\n");
	}
	keymap->keycode = 0;
	keymap->modifier = 0;
	return keymap;
}

int tp_init_keylist(touchpanel *tp, int size)
{
	touch_keymap *keymap;
	touch_keymap **keymap_list;
	int i;

	keymap_list = (touch_keymap **)calloc(size, sizeof(touch_keymap *));

	tp->keymap_list = keymap_list;

	if (keymap_list == NULL) {
		printf("Keymap list memory allocation failed\n");
		return 0;
	}

	for (i = 0; i < size; i++) {
		keymap =  tp_new_keymap();
		if (keymap == NULL) {
			printf("Keymap memory allocation failed\n");
		}
		keymap_list[i] = keymap;
		tp->no_keys++;
	}
	return 1;
}

int tp_load_keymaps(touchpanel *tp)
{
	int i;

	// FIXME: Should be loaded from command line
	tp->keymap_list[0]->keycode = KEY1;
	tp->keymap_list[0]->modifier = MOD1;
	tp->keymap_list[1]->keycode = KEY2;
	tp->keymap_list[1]->modifier = MOD2;
	tp->keymap_list[2]->keycode = KEY3;
	tp->keymap_list[2]->modifier = MOD3;

#ifdef DEBUG
	for (i = 0; i < tp->no_keys; i++) {
		printf("\tkey: %d\tmodifier: %d\n", 
		       tp->keymap_list[i]->keycode,
		       tp->keymap_list[i]->modifier);
	}
#endif

	return 1;
}

int tp_listen_keys(touchpanel *tp, int grab)
{
	int i;

	/* We cannot use XButtonPressMask, it's already taken */
	XSelectInput(tp->display,
		     tp->window,
		     (EnterWindowMask | PointerMotionMask));	

	for (i = 0; i < tp->no_keys; i++) {
		if (grab) {
			XGrabKey(tp->display, 
				 tp->keymap_list[i]->keycode,
				 tp->keymap_list[i]->modifier,
				 tp->window,
				 False,
				 GrabModeAsync,
				 GrabModeAsync);
		} else {
			XUngrabKey(tp->display, 
				   tp->keymap_list[i]->keycode,
				   tp->keymap_list[i]->modifier,
				   tp->window);
		}
	}
}

void tp_signal_handler(int sig)
{
	printf("Got signal %d\n", sig);
	switch (sig) {
	case SIGINT:
		tp_exit(tp, 0);
		break;
	default:
		printf("Got unhandled signal\n");
	}
}

int parse_args(touchpanel *tp, int argc, char *argv[])
{
	int i;
	
	for (i = 1; i < argc; i++) {
		if (!(strcmp(argv[i], "-display")) 
		    || !(strcmp(argv[i], "-d"))) {
			if (i++ < argc) {
				if (strlen(argv[i]) > 512)
				    return;
				tp->display_name = argv[i];
			}
		}
	}
}

touchpanel * tp_init(int argc, char *argv[])
{
	touchpanel *tp = NULL;
	int i, ret;

	(void)signal(SIGINT, tp_signal_handler);

	tp = (touchpanel *)malloc(sizeof(touchpanel));
	if (tp == NULL) {
		printf("Could not allocate memory\n");
		tp_exit(tp, 10);
	}

	parse_args(tp, argc, argv);

	tp->display = XOpenDisplay(tp->display_name);
	if (tp->display == NULL) {
		printf("Could not open display\n");
		tp_exit(tp, 11);
	}

	tp->screen = DefaultScreen(tp->display);
	tp->window = RootWindow(tp->display, tp->screen);
	tp->no_keys = 0;
	tp->no_buttons = 0;
	tp->queued = 0;

	ret = XTestGrabControl(tp->display, True);
	if (ret != 1) {
		printf("Could not grab display\n");
		tp_exit(tp, 12);
	}

	ret = tp_init_keylist(tp, NUM_PSION_MOUSEBUTTONS);
	if (ret != 1) {
		printf("Keymap memory allocation failed\n");
	}

	ret = tp_load_keymaps(tp);
	ret = tp_listen_keys(tp, 1);

	tp->orig_map = (unsigned char *)calloc(NUM_MAX_MOUSEBUTTONS, 
					       sizeof(unsigned char));
	tp->new_map = (unsigned char *)calloc(NUM_MAX_MOUSEBUTTONS, 
					       sizeof(unsigned char));
	
	for (i = 0; i < NUM_MAX_MOUSEBUTTONS; i++) {
		tp->new_map[i] = 0;
	}

	return tp;
}

void tp_exit(touchpanel *tp, int code) {
	int ret;

	ret = tp_listen_keys(tp, 0);

	free(tp);
	exit(code);
}
