/*
 *
 * Sony Programmable I/O Control Device driver for VAIO. 
 * Userspace X11 Daemon Utility
 *
 * Copyright (C) 2001-2003 Stelian Pop <stelian@popies.net>
 *
 * Copyright (C) 2001-2002 Alcove <www.alcove.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <getopt.h>

#include <X11/Xlib.h>
#include <X11/extensions/XTest.h>

#include "sonypi.h"

static char *version = "version 1.9.1, Fev 20, 2004";

static void inline simulateKeyPress(Display *disp, char *keyname) {
	KeyCode keycode = 0;
	keycode = XKeysymToKeycode(disp, XStringToKeysym(keyname));
	XTestGrabControl(disp, True);
	XTestFakeKeyEvent(disp, keycode, True, 0);
	XSync(disp, False);
	XTestGrabControl(disp, False);
}

static void inline simulateKeyRelease(Display *disp, char *keyname) {
	KeyCode keycode = 0;
	keycode = XKeysymToKeycode(disp, XStringToKeysym(keyname));
	XTestGrabControl(disp, True);
	XTestFakeKeyEvent(disp, keycode, False, 0);
	XSync(disp, False);
	XTestGrabControl(disp, False);
}

#define BUTTON_PRESSED	1
#define BUTTON_RELEASED	2

static void inline simulateButton(Display *disp, int button, int mask) {
	XTestGrabControl(disp, True);
	if (mask & BUTTON_PRESSED)
		XTestFakeButtonEvent(disp, button, True, 0);
	if (mask & BUTTON_RELEASED)
		XTestFakeButtonEvent(disp, button, False, 0);
	XSync(disp, False);
	XTestGrabControl(disp, False);
}

static void inline showHelp (void)
{
     fprintf (stdout, "Sony Programmable I/O Control Device driver for VAIO\n");
     fprintf (stdout, "Userspace X11 Daemon Utility\n");
     fprintf (stdout, "\n");
     fprintf (stdout, "options:\n");
     fprintf (stdout, " -D, --daemon   Start as daemon\n");
     fprintf (stdout, " -h, --help     Print this help\n");
     fprintf (stdout, " -v, --version  Show version\n");
}

static void inline verboseEvent(char *event) {
	fprintf(stderr, "Event: %s\n", event);
}

/*
 * Main function.
 */
int main(int argc, char *argv[]) {

        int fd;
	fd_set rfds;
	int retval;
	unsigned char event;
        Display *disp = NULL;
	int jogdial_pressed = 0;
     
     
        while (1) {
	     	     
	     static struct option long_options[] = {
		  {"daemon", 0, 0, 'D'},
		  {"help", 0, 0, 'h'},
		  {"version", 0, 0, 'v'},
		  {0, 0, 0, 0}
	     };
	  
	     int c = getopt_long (argc, argv, "hDv",
			      long_options, 0);
	     if (c == -1)
		  break;
	     
	     switch (c) {
		  
	     case 'D':
		  if (fork()) { exit (0); }
		  break;
		  
	     case 'h':
		  showHelp();
		  exit (0);
		  
	     case 'v':
		  fprintf (stdout, "Sony Vaio SPIC daemon %s\n", version);
		  exit (0);
		  
	     default:
		  showHelp();
		  exit (0);
	     }
	}

     
        fprintf(stderr, "%s: Sony Vaio SPIC daemon %s\n", argv[0], version);

	disp = XOpenDisplay(NULL);
	if (!disp) {
		fprintf(stderr, "Failed to open display\n");
#ifdef FORCE_X
		return -1;
#endif
	}
	if ( (fd = open("/dev/sonypi", O_RDONLY)) == -1) {
		fprintf(stderr, "Failed to open /dev/sonypi: %s\n", 
			strerror(errno));
		return -1;
	}

again:
	FD_ZERO(&rfds);
	FD_SET(fd, &rfds);

	retval = select(fd + 1, &rfds, NULL, NULL, NULL);

	if (retval == -1) {
		fprintf(stderr, "select failed: %s\n", strerror(errno));
		return -1;
	}

	if (FD_ISSET(fd, &rfds)) {
		if (read(fd, &event, sizeof(event)) != sizeof(event)) {
			fprintf(stderr, "read failed: %s\n", strerror(errno));
			return -1;
		}
		switch (event) {
			case SONYPI_EVENT_JOGDIAL_DOWN:
				if( disp ) {
					simulateButton(disp, 5, BUTTON_PRESSED | BUTTON_RELEASED);
				};
				verboseEvent("Jogdial Down");
				break;
			case SONYPI_EVENT_JOGDIAL_UP:
				if( disp ) {
					simulateButton(disp, 4, BUTTON_PRESSED | BUTTON_RELEASED);
				};
				verboseEvent("Jogdial Up");
				break;
			case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED:
				verboseEvent("Jogdial Down & Pressed");
				break;
			case SONYPI_EVENT_JOGDIAL_UP_PRESSED:
				verboseEvent("Jogdial Up & Pressed");
				break;
			case SONYPI_EVENT_JOGDIAL_PRESSED:
				verboseEvent("Jogdial Pressed");
				if (jogdial_pressed)
					break;
				if( disp ) {
					simulateButton(disp, 2, BUTTON_PRESSED);
				};
				jogdial_pressed = 1;
				break;
			case SONYPI_EVENT_JOGDIAL_RELEASED:
				verboseEvent("Jogdial Released");
				if (!jogdial_pressed)
					break;
				if( disp ) {
					simulateButton(disp, 2, BUTTON_RELEASED);
				};
				jogdial_pressed = 0;
				break;
			case SONYPI_EVENT_CAPTURE_PRESSED:
				if (!fork()) {
					execlp("xterm", "xterm", 0);
					exit(0);
				}
				verboseEvent("Capture Pressed");
				break;
			case SONYPI_EVENT_CAPTURE_RELEASED:
				verboseEvent("Capture Released");
				break;
			case SONYPI_EVENT_CAPTURE_PARTIALPRESSED:
				verboseEvent("Capture Partial Pressed");
				break;
			case SONYPI_EVENT_CAPTURE_PARTIALRELEASED:
				verboseEvent("Capture Partial Released");
				break;
			case SONYPI_EVENT_FNKEY_ESC:
				verboseEvent("Fn-Esc Pressed");
				break;
			case SONYPI_EVENT_FNKEY_F1:
				verboseEvent("Fn-F1 Pressed");
				break;
			case SONYPI_EVENT_FNKEY_F2:
				verboseEvent("Fn-F2 Pressed");
				break;
			case SONYPI_EVENT_FNKEY_F3:
				verboseEvent("Fn-F3 Pressed");
				break;
			case SONYPI_EVENT_FNKEY_F4:
				verboseEvent("Fn-F4 Pressed");
				break;
			case SONYPI_EVENT_FNKEY_F5:
				verboseEvent("Fn-F5 Pressed");
				break;
			case SONYPI_EVENT_FNKEY_F6:
				verboseEvent("Fn-F6 Pressed");
				break;
			case SONYPI_EVENT_FNKEY_F7:
				verboseEvent("Fn-F7 Pressed");
				break;
			case SONYPI_EVENT_FNKEY_F8:
				verboseEvent("Fn-F8 Pressed");
				break;
			case SONYPI_EVENT_FNKEY_F9:
				verboseEvent("Fn-F9 Pressed");
				break;
			case SONYPI_EVENT_FNKEY_F10:
				verboseEvent("Fn-F10 Pressed");
				break;
			case SONYPI_EVENT_FNKEY_F11:
				verboseEvent("Fn-F11 Pressed");
				break;
			case SONYPI_EVENT_FNKEY_F12:
				verboseEvent("Fn-F12 Pressed");
				break;
			case SONYPI_EVENT_FNKEY_1:
				verboseEvent("Fn-1 Pressed");
				break;
			case SONYPI_EVENT_FNKEY_2:
				verboseEvent("Fn-2 Pressed");
				break;
			case SONYPI_EVENT_FNKEY_D:
				verboseEvent("Fn-D Pressed");
				break;
			case SONYPI_EVENT_FNKEY_E:
				verboseEvent("Fn-E Pressed");
				break;
			case SONYPI_EVENT_FNKEY_F:
				verboseEvent("Fn-F Pressed");
				break;
			case SONYPI_EVENT_FNKEY_S:
				verboseEvent("Fn-S Pressed");
				break;
			case SONYPI_EVENT_FNKEY_B:
				verboseEvent("Fn-B Pressed");
				break;
			case SONYPI_EVENT_BLUETOOTH_PRESSED:
				verboseEvent("Bluetooth Button Pressed");
				break;
#ifdef SONYPI_EVENT_PKEY_P1
			case SONYPI_EVENT_PKEY_P1:
				verboseEvent("P1 Pressed");
				break;
#endif
#ifdef SONYPI_EVENT_PKEY_P2
			case SONYPI_EVENT_PKEY_P2:
				verboseEvent("P2 Pressed");
				break;
#endif
#ifdef SONYPI_EVENT_PKEY_P3
			case SONYPI_EVENT_PKEY_P3:
				verboseEvent("P3 Pressed");
				break;
#endif
#ifdef SONYPI_EVENT_BACK_PRESSED
			case SONYPI_EVENT_BACK_PRESSED:
				verboseEvent("Back Button Pressed");
				break;
#endif
#ifdef SONYPI_EVENT_LID_OPENED
			case SONYPI_EVENT_LID_OPENED:
				verboseEvent("Lid Opened");
				break;
#endif
#ifdef SONYPI_EVENT_LID_CLOSED
			case SONYPI_EVENT_LID_CLOSED:
				verboseEvent("Lid Closed");
				break;
#endif
#ifdef SONYPI_EVENT_BLUETOOTH_ON
			case SONYPI_EVENT_BLUETOOTH_ON:
				verboseEvent("Bluetooth set to on");
				break;
#endif
#ifdef SONYPI_EVENT_BLUETOOTH_OFF
			case SONYPI_EVENT_BLUETOOTH_OFF:
				verboseEvent("Bluetooth set to off");
				break;
#endif
#ifdef SONYPI_EVENT_HELP_PRESSED
			case SONYPI_EVENT_HELP_PRESSED:
				verboseEvent("Help button pressed");
				break;
#endif
#ifdef SONYPI_EVENT_FNKEY_ONLY
			case SONYPI_EVENT_FNKEY_ONLY:
				verboseEvent("Fn button pressed");
				break;
#endif
#ifdef SONYPI_EVENT_JOGDIAL_FAST_DOWN
			case SONYPI_EVENT_JOGDIAL_FAST_DOWN:
				verboseEvent("Jogdial Fast Down");
				break;
#endif
#ifdef SONYPI_EVENT_JOGDIAL_FAST_UP
			case SONYPI_EVENT_JOGDIAL_FAST_UP:
				verboseEvent("Jogdial Fast Up");
				break;
#endif
#ifdef SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED
			case SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED:
				verboseEvent("Jogdial Fast Down Pressed");
				break;
#endif
#ifdef SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED
			case SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED:
				verboseEvent("Jogdial Fast Up Pressed");
				break;
#endif
#ifdef SONYPI_EVENT_JOGDIAL_VFAST_DOWN
			case SONYPI_EVENT_JOGDIAL_VFAST_DOWN:
				verboseEvent("Jogdial Very Fast Down");
				break;
#endif
#ifdef SONYPI_EVENT_JOGDIAL_VFAST_UP
			case SONYPI_EVENT_JOGDIAL_VFAST_UP:
				verboseEvent("Jogdial Very Fast Up");
				break;
#endif
#ifdef SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED
			case SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED:
				verboseEvent("Jogdial Very Fast Down Pressed");
				break;
#endif
#ifdef SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED
			case SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED:
				verboseEvent("Jogdial Very Fast Up Pressed");
				break;
#endif
#ifdef SONYPI_EVENT_ZOOM_PRESSED
			case SONYPI_EVENT_ZOOM_PRESSED:
				verboseEvent("Zoom button pressed");
				break;
#endif
#ifdef SONYPI_EVENT_THUMBPHRASE_PRESSED
			case SONYPI_EVENT_THUMBPHRASE_PRESSED:
				verboseEvent("Thumbphrase button pressed");
				break;
#endif
#ifdef SONYPI_EVENT_MEYE_FACE
			case SONYPI_EVENT_MEYE_FACE:
				verboseEvent("Motion Eye camera turned");
				break;
#endif
#ifdef SONYPI_EVENT_MEYE_OPPOSITE
			case SONYPI_EVENT_MEYE_OPPOSITE:
				verboseEvent("Motion Eye camera turned opposite");
				break;
#endif
#ifdef SONYPI_EVENT_MEMORYSTICK_INSERT
			case SONYPI_EVENT_MEMORYSTICK_INSERT:
				verboseEvent("MemoryStick inserted");
				break;
#endif
#ifdef SONYPI_EVENT_MEMORYSTICK_EJECT
			case SONYPI_EVENT_MEMORYSTICK_EJECT:
				verboseEvent("MemoryStick ejected");
				break;
#endif
#ifdef SONYPI_EVENT_ANYBUTTON_RELEASED
			case SONYPI_EVENT_ANYBUTTON_RELEASED:
				verboseEvent("Any button released");

				if (!jogdial_pressed)
					break;
				if( disp ) {
					simulateButton(disp, 2, BUTTON_RELEASED);
				};
				jogdial_pressed = 0;
				break;
#endif
#ifdef SONYPI_EVENT_BATTERY_INSERT
			case SONYPI_EVENT_BATTERY_INSERT:
				verboseEvent("Battery inserted");
				break;
#endif
#ifdef SONYPI_EVENT_BATTERY_REMOVE
			case SONYPI_EVENT_BATTERY_REMOVE:
				verboseEvent("Battery removed");
				break;
#endif
			default:
				verboseEvent("Event unknown!");
		}
	}

	goto again;

	close(fd);
	return 0;
}
