#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <error.h>

#define DELAY 10

#define KEY 1
#define KEY1 256
#define KEY2 257
#define KEY3 258
#define KEY4 259
#define KEY5 260
#define KEY6 261
#define KEY7 262
#define KEY8 263
#define KEY9 264
#define KEY10 265
#define KEY11 266
#define KEY12 267
#define KEY13 268

#define JOGSHUTTLE 2
#define JOG 7
#define SHUTTLE 8

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <linux/input.h>
#include <X11/Xlib.h>
#include <X11/extensions/XTest.h>
#include <X11/keysym.h>

typedef struct input_event EV;

unsigned short jogvalue = 0xffff;
int shuttlevalue = 0xffff;

Display *display;

void initdisplay()
{
  int event, error, major, minor;
  display = XOpenDisplay(0);
  if (!display)
  {
    fprintf(stderr, "unable to open X display\n");
    exit(1);
  }
  if (!XTestQueryExtension(display, &event, &error, &major, &minor))
  {
    fprintf(stderr, "Xtest extensions not supported\n");
    XCloseDisplay(display);
    exit(1);
  }
}

void sendkey(KeySym modifier, KeySym key)
{
  KeyCode modifiercode;
  KeyCode keycode;
  if (modifier != 0)
  {
    modifiercode = XKeysymToKeycode(display, modifier);
    XTestFakeKeyEvent(display, modifiercode, True, DELAY);
  }
  keycode = XKeysymToKeycode(display, key);
  XTestFakeKeyEvent(display, keycode, True, DELAY);
  XTestFakeKeyEvent(display, keycode, False, DELAY);
  if (modifier != 0)
    XTestFakeKeyEvent(display, modifiercode, False, DELAY);
  XFlush(display);
}

void key(unsigned short code, unsigned int value)
{
  if (value == 0)
    return;
  switch (code)
  {
    case KEY1 : sendkey(0, XK_KP_3); break;
    case KEY2 : sendkey(0, XK_KP_0); break;
    case KEY3 : sendkey(0, XK_KP_Add); break;
    case KEY4 : sendkey(0, XK_KP_Enter); break;
    case KEY5 : sendkey(XK_Mode_switch, '['); break;
    case KEY6 : sendkey(XK_Mode_switch, ']'); break;
    case KEY7 : sendkey(0, 'v'); break;
    case KEY8 : sendkey(0, XK_KP_4); break;
    case KEY9 : sendkey(0, XK_KP_1); break;
  }
}

void jog(unsigned int value)
{
  if (jogvalue != 0xffff)
  {
    if (value < jogvalue)
      sendkey(0, XK_KP_4);
    else if (value > jogvalue)
      sendkey(0, XK_KP_1);
  }
  jogvalue = value;
}

void shuttle(int value)
{
  int i = value/2;
  if (i == shuttlevalue)
    return;
  switch(i)
  {
    case -2 : sendkey(0, XK_KP_Add); break;
    case -1 : sendkey(0, XK_KP_6); break;
    case  0 : sendkey(0, XK_KP_0); break;
    case  1 : sendkey(0, XK_KP_3); break;
    case  2  : sendkey(0, XK_KP_Enter); break;
  }
  shuttlevalue = i;
}

void jogshuttle(unsigned short code, unsigned int value)
{
  switch (code)
  {
    case JOG     : jog(value); break;
    case SHUTTLE : shuttle(value); break;
  }
}

void handle_event(EV ev)
{
  switch (ev.type)
  {
    case KEY        : key(ev.code, ev.value);
    case JOGSHUTTLE : jogshuttle(ev.code, ev.value);
  }
}

int main(int argc, char **argv)
{
  int fd;
  char name[256] = "unknown";
  EV ev;
  if (argc < 2)
  {
    fprintf(stderr, "syntax: cshuttle <device>\n");
    return 1;
  }
  printf("cshuttle 0.1beta written by Arendt David (admin@prnet.org)\n");
  fd = open(argv[1], O_RDONLY);
  if (fd < 0)
  {
    fprintf(stderr, "unable to open %s\n", argv[1]);
    return 1;
  }
  if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0)
  {
    perror("evdev ioctl");
  }
  printf("device name: %s\n", name);
  initdisplay();
  while (1)
  {
    read(fd, &ev, sizeof(ev));
    handle_event(ev);
  }
  close(fd);
  return 0;
}

