/* 
Ê* ztsd - Convert Zaurus touchscreen events into iPAQ touchscreen 
 Ê* events 
 Ê* 
 Ê* ipaq_ts_dev should be a named pipe (fifo), from which programs 
 Ê* (such as the X server) will read touchscreen events 
 Ê* 
 Ê* Automatically calibrates based on coordinate minima and maxima. 
 Ê* Drag toward each corner to ensure proper calibration. 
 Ê* 
 Ê* Matt Zimmerman <[EMAIL PROTECTED]>, 07/2002 
 Ê* 
 Ê* Descriptive comments and miscellaneous fixes by David Anders 
 Ê* <[EMAIL PROTECTED]> 
 Ê* 
 Ê*/


#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <signal.h>


typedef struct 
{
	unsigned short pressure;
	unsigned short x; 
	unsigned short y; 
	unsigned short pad; 
} TS_EVENT;


typedef union zaurus_event
{
	struct sl5500
		{
			long x;
			long y; 
			long pressure; 
			long long millisecs; 
		} sl5500;
	struct c860
		{
			short pressure; 
			short x;
			short y; 
			short millisecs; 
		} c860;
} TS_EVENT_ZAURUS;


static int daemonize = 1;


/* /dev/sharp_ts is the touch screen node used on the Sharp 
Ê* and OpenZaurus builds. 
Ê*/ 
static const char *real_ts_dev = "/dev/sharp_ts";


/* /dev/ts should be a named pipe (fifo) 
Ê*/ 
static const char *ipaq_ts_dev = "/dev/ets"; 
static const char *pidfile = "/var/run/ztsd.pid";


static int calibrated = 0; 
static long min_x = 10000, max_x = 0, min_y = 10000, max_y = 0;


static int debug = 0;


static void convert_event_5500(const TS_EVENT_ZAURUS *zaurus_event, 
						  TS_EVENT *ipaq_event) {
	
	
	/* This was determined by using the ipaq event structure from the 
	* xfree86 code and comparing it to the touch screen header files 
	* in the kernel for the zaurus, improved through trial and error 
	*/
	
	
	ipaq_event->y = (240 * (zaurus_event->sl5500.x - min_x)) / (max_x - min_x); 
	ipaq_event->x = 320 - (320 * ((zaurus_event->sl5500.y - min_y)) / (max_y - min_y)); 
	ipaq_event->pressure = zaurus_event->sl5500.pressure;
	
	
	if (debug) 
		fprintf(stderr, "converted: x=%ld, y=%ld, pressure=%ld\n", 
				ipaq_event->x, ipaq_event->y, ipaq_event->pressure); 
}

static void convert_event_860(const TS_EVENT_ZAURUS *zaurus_event, 
							   TS_EVENT *ipaq_event) {
	
	
	/* This was determined by using the ipaq event structure from the 
	* xfree86 code and comparing it to the touch screen header files 
	* in the kernel for the zaurus, improved through trial and error 
	*/
	
	
	ipaq_event->y = (240 * (zaurus_event->c860.x - min_x) + (max_x - min_x)/2) / (max_x - min_x); 
	ipaq_event->x = 320 - ((320 * (zaurus_event->c860.y - min_y) + (max_y - min_y)/2) / (max_y - min_y)); 
	ipaq_event->pressure = zaurus_event->c860.pressure;
	
	
	if (debug) 
		fprintf(stderr, "converted: x=%ld, y=%ld, pressure=%ld\n", 
				ipaq_event->x, ipaq_event->y, ipaq_event->pressure); 
}


static void calibrate(long x, long y) { 
	if (x < min_x) 
		min_x = x; 
	else if (x > max_x) 
		max_x = x; 
	if(max_x <= min_x)
		max_x = min_x+1;	// avoid division by 0
	if (y < min_y) 
		min_y = y; 
	else if (y > max_y) 
		max_y = y;
	if(max_y <= min_y)
		max_y = min_y+1;	// avoid division by 0
	
	
	if (debug) 
		fprintf(stderr, "calibrate: x=(%ld,%ld) y=(%ld,%ld)\n", 
				min_x, max_x, min_y, max_y); 
}


int main(int argc, char *argv[]) { 
	int ch; 
	int real_fd = -1; 
	int ipaq_fd = -1; 
	int pidfile_fd = -1;
	
	signal(SIGUSR1, SIG_IGN);	// ignore...

	while ((ch = getopt(argc, argv, "fhvd")) != -1) { 
		switch (ch) { 
			case 'd': 
				debug = 1; 
				/* fall through */ 
			case 'f': 
				daemonize = 0; 
				break; 
			case 'h': 
				fprintf(stderr, "ztsd is used to translate Zaurus touch\n"); 
				fprintf(stderr, "screen events into iPAQ events for use with an\n"); 
				fprintf(stderr, "XFree86 built using the iPAQ touch screen driver\n"); 
				fprintf(stderr, "Options:\n"); 
				fprintf(stderr, "-d - debug\n"); 
				fprintf(stderr, "-f - don't fork\n"); 
				fprintf(stderr, "-v - version\n"); 
				return 0; 
			case 'v': 
				printf("ztsd 0.2\n"); 
				return 0; 
		} 
	}
	
	
	real_fd = open(real_ts_dev, O_RDONLY); 
	if (real_fd == -1) { 
		perror(real_ts_dev); 
		return -1; 
	}
	
	
	ipaq_fd = open(ipaq_ts_dev, O_RDWR); 
	if (ipaq_fd == -1) { 
		perror(ipaq_ts_dev); 
		return -1; 
	}
	
	
	if (daemonize) { 
		switch (fork()) { 
			case 0: 
				daemon(0,0); 
				break; 
			case -1: 
				perror("fork"); 
				return -1; 
			default: 
				return 0; 
		} 
	}
	
	
	pidfile_fd = open(pidfile, O_WRONLY|O_CREAT, 0644); 
	if (pidfile_fd == -1) { 
		perror(pidfile); 
		return -1; 
	}
	
	
	/* XXX GNU-ism */ 
	dprintf(pidfile_fd, "%ld", getpid());
	
	
	close(pidfile_fd); 
	
	if(debug)
		fprintf(stderr, "initialized as %ld\n", getpid());
	
	for(;;) { 
		static TS_EVENT_ZAURUS zaurus_event; 
		static TS_EVENT ipaq_event; 
		static int ret;
		
		
		memset(&ipaq_event, 0, sizeof(ipaq_event)); 
		memset(&zaurus_event, 0, sizeof(zaurus_event));
		
		
		ret = read(real_fd, &zaurus_event, sizeof(zaurus_event));
		
		
		if (ret == -1) { 
			perror("read"); 
			return -1; 
		} 
		if(ret == sizeof(zaurus_event.sl5500))
			{
			if (debug) 
				fprintf(stderr, "x: %ld, y: %ld, pressure: %ld, ms: %ld\n", 
						zaurus_event.sl5500.x, zaurus_event.sl5500.y, zaurus_event.sl5500.pressure, 
						zaurus_event.sl5500.millisecs);
			
			calibrate(zaurus_event.sl5500.x, zaurus_event.sl5500.y); 
			convert_event_5500(&zaurus_event, &ipaq_event);
			}
		else if (ret == sizeof(zaurus_event.c860))
			{ // short format (e.g. on C860)
			if (debug) 
				fprintf(stderr, "x: %ld, y: %ld, pressure: %ld, ms: %ld\n", 
						zaurus_event.c860.x, zaurus_event.c860.y, zaurus_event.c860.pressure, 
						zaurus_event.c860.millisecs);
			
			calibrate(zaurus_event.c860.x, zaurus_event.c860.y); 
			convert_event_860(&zaurus_event, &ipaq_event);
			}
		else
			{ 
			if(debug)
				fprintf(stderr, "read partial event (%d : %d)\n", ret, sizeof(zaurus_event));
			continue; 
			}
		
		
				
		
		ret = write(ipaq_fd, &ipaq_event, sizeof(ipaq_event));
		
		
		if (ret == -1) { 
			perror("write"); 
			return -1; 
		} 
	}
	
	
	/* NOT REACHED */ 
	
	return 0; 
}