/*      Key driver, by Jonathan Clark 
          (portions from showkey.c)
*/

#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <signal.h>
#include <fcntl.h>
#include <termios.h>
#include <linux/kd.h>
#include <linux/vt.h>
#include <fcntl.h>
#include <sys/stat.h>



unsigned char keyboard_buf[16];

#include <linux/keyboard.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <string.h>

int fd,my_console=-1;
struct termios old_term;

void clean_up(int close_fd) {
	ioctl(fd, KDSKBMODE, K_XLATE);

	tcsetattr(fd, TCSAFLUSH, &old_term);
        if (close_fd)
  	  close(fd);
        if (close_fd)
          exit(0);
}

void go_raw(int open_fd)
{
	struct termios new_term;
        struct vt_stat v;
        if (open_fd)
        {
  		if ((fd = open("/dev/console", O_RDONLY)) < 0) {
			perror("/dev/console");
			exit(1);
		}
	}
        if (my_console==-1)
        {
          ioctl(fd,VT_GETSTATE,&v);  /* see which console we are using */ 
          my_console=v.v_active;
        }
        ioctl(fd,VT_WAITACTIVE,my_console);  /* wait for our console to come back */

	tcgetattr(fd, &new_term);
	tcgetattr(fd, &old_term);

	new_term.c_lflag = new_term.c_lflag & ~ (ICANON | ECHO | ISIG );
	new_term.c_cc[VMIN] = 1; //sizeof(keyboard_buf);
	new_term.c_cc[VTIME] = 0;	/* 0.1 sec intercharacter timeout */

	tcsetattr(fd, TCSAFLUSH, &new_term);
	if (ioctl(fd, KDSKBMODE,
		K_RAW)) {
		perror("KDSKBMODE");
		exit(1);
	}
        system("stty raw </dev/console >/dev/console");

}

void die(int x) {
        fprintf(stderr,"die %d\n",x);
   
	clean_up(1);
	exit(1);
}

int key_watch(int write_fd) 
{
        unsigned char map[128];

	int i, n,lalt,ralt,switch_to,lctrl,rctrl,up,quit;


	/*
		if we receive a signal, we want to exit nicely, in
		order not to leave the keyboard in an unusable mode
	*/
	signal(SIGHUP, die);
	signal(SIGINT, die);
	signal(SIGQUIT, die);
	signal(SIGILL, die);
	signal(SIGTRAP, die);
	signal(SIGABRT, die);
	signal(SIGIOT, die);
	signal(SIGFPE, die);
	signal(SIGKILL, die);
	signal(SIGUSR1, die);
	signal(SIGSEGV, die);
	signal(SIGUSR2, die);
	signal(SIGPIPE, die);
	signal(SIGTERM, die);
	signal(SIGSTKFLT, die);
	signal(SIGCHLD, die);
	signal(SIGCONT, die);
	signal(SIGSTOP, die);
	signal(SIGTSTP, die);
	signal(SIGTTIN, die);
	signal(SIGTTOU, die);

        go_raw(1);
        quit=lalt=ralt=lctrl=rctrl=0;
        memset(map,0,128);
	while (!quit) {
		n = read(fd, keyboard_buf, sizeof(keyboard_buf));
		for (i = 0; i < n; i++) 
                { up=!(keyboard_buf[i]&0x80);
                  if ((keyboard_buf[i]&0x7f)==56) lalt=up; 
                  if ((keyboard_buf[i]&0x7f)==96) ralt=up;
                  if ((keyboard_buf[i]&0x7f)==29) lctrl=up;
                  if (up || up!=map[(keyboard_buf[i]&0x7f)])
                  {
                    map[(keyboard_buf[i]&0x7f)]=up;
                    if (!write(write_fd,&keyboard_buf[i],1))
		    {
		      fprintf(stderr,"keydrv : unable to write to parent (cleaning up)\n");
		      clean_up(1);
		      exit(0);
		    }
                  }
                }
		for (i = 0; i < n; i++) 
                {
                  if ((lalt || ralt) && (keyboard_buf[i]&0x7f)>=59 &&
                      (keyboard_buf[i]&0x7f)<=68)
                  { 

                    switch_to=(keyboard_buf[i]&0x7f)-58;
                    clean_up(0);
		    
                    ioctl(fd,VT_ACTIVATE,switch_to);
                    go_raw(0); 
                  }
                  if ((lctrl || rctrl) && (keyboard_buf[i]&0x7f)==46)
                  {
                    fprintf(stderr,"^C\n"); fflush(stderr);
                    quit=1;
                  }
                }

	}
	clean_up(1);
	exit(0);
}


main()
{
  int f,chd;
  unlink("/tmp/jckey-driver");
  if (mkfifo("/tmp/jckey-driver",S_IRWXU))
  { perror("Key driver : unable to make fifo /tmp/jckey-driver");
    exit(1);
  }
  chd=fork();
  if (chd) 
  { printf("%d\n",chd);
    return 0;
  }
  f=open("/tmp/jckey-driver",O_WRONLY);
  if (f<0)
  { perror("/tmp/jckey-driver"); 
    exit(1);
  }
  key_watch(f);
  close(f);
  unlink("/tmp/jckey-driver");
}


