#include <windows.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <ctype.h>

#if 0                           /* See windows.h, WIN.H */
#define VK_SUBTRACT     0x6D
#define VK_DECIMAL      0x6E
#define VK_DIVIDE       0x6F
#endif

static struct {
  int vk;
  const char *val[4];
} keytable[] = {
               /* NORMAL */  /* SHIFT */    /* CTRL */       /* ALT */
  {VK_LEFT,     {"\033[D",      "\033[D",       "\033[D",       "\033\033[D"}},
  {VK_RIGHT,    {"\033[C",      "\033[C",       "\033[C",       "\033\033[C"}},
  {VK_UP,       {"\033[A",      "\033[A",       "\033[A",       "\033\033[A"}},
  {VK_DOWN,     {"\033[B",      "\033[B",       "\033[B",       "\033\033[B"}},
  {VK_PRIOR,    {"\033[5~",     "\033[5~",      "\033[5~",      "\033\033[5~"}},
  {VK_NEXT,     {"\033[6~",     "\033[6~",      "\033[6~",      "\033\033[6~"}},
  {VK_HOME,     {"\033[1~",     "\033[1~",      "\033[1~",      "\033\033[1~"}},
  {VK_END,      {"\033[4~",     "\033[4~",      "\033[4~",      "\033\033[4~"}},
  {VK_INSERT,   {"\033[2~",     "\033[2~",      "\033[2~",      "\033\033[2~"}},
  {VK_DELETE,   {"\033[3~",     "\033[3~",      "\033[3~",      "\033\033[3~"}},
  {VK_F1,       {"\033[[A",     "\033[23~",     NULL,           NULL}},
  {VK_F2,       {"\033[[B",     "\033[24~",     NULL,           NULL}},
  {VK_F3,       {"\033[[C",     "\033[25~",     NULL,           NULL}},
  {VK_F4,       {"\033[[D",     "\033[26~",     NULL,           NULL}},
  {VK_F5,       {"\033[[E",     "\033[28~",     NULL,           NULL}},
  {VK_F6,       {"\033[17~",    "\033[29~",     "\036",         NULL}},
  {VK_F7,       {"\033[18~",    "\033[31~",     NULL,           NULL}},
  {VK_F8,       {"\033[19~",    "\033[32~",     NULL,           NULL}},
  {VK_F9,       {"\033[20~",    "\033[33~",     NULL,           NULL}},
  {VK_F10,      {"\033[21~",    "\033[34~",     NULL,           NULL}},
  {VK_F11,      {"\033[23~",    NULL,           NULL,           NULL}},
  {VK_F12,      {"\033[24~",    NULL,           NULL,           NULL}},
  {VK_NUMPAD5,  {"\033[G",      NULL,           NULL,           NULL}},
  {VK_CLEAR,    {"\033[G",      NULL,           NULL,           NULL}},
  {'6',         {NULL,          NULL,           "\036",         NULL}},
#define VK_GREYDIV 191
#define VK_GREYENT 13
  {VK_GREYDIV,  {"\033[0o",     "\033[0o",      "\033[0o",      "\033[0o"}},
  {VK_MULTIPLY, {"\033[0j",     "\033[0j",      "\033[0j",      "\033[0j"}},
  {VK_SUBTRACT, {"\033[0m",     "\033[0m",      "\033[0m",      "\033[0m"}},
  {VK_ADD,      {"\033[0k",     "\033[0k",      "\033[0k",      "\033[0k"}},
  {VK_GREYENT,  {"\033[M",      "\033[M",       "\033[M",       "\033[M"}},
  {0,           {"",            NULL,           NULL,           NULL}}
};

const char *
get_nonascii_key (INPUT_RECORD &input_rec, char *tmp)
{
#define NORMAL  0
#define SHIFT   1
#define CONTROL 2
#define ALT     3
  int i;
  int modifier_index = NORMAL;

  if (input_rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)
    modifier_index = SHIFT;
  else if (input_rec.Event.KeyEvent.dwControlKeyState &
                (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
    modifier_index = CONTROL;
  else if (input_rec.Event.KeyEvent.dwControlKeyState &
                (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
    modifier_index = ALT;

  for (i = 0; keytable[i].vk; i++)
    if (input_rec.Event.KeyEvent.wVirtualKeyCode == keytable[i].vk)
      return keytable[i].val[modifier_index];

  if (input_rec.Event.KeyEvent.uChar.AsciiChar)
    {
      tmp[0] = input_rec.Event.KeyEvent.uChar.AsciiChar;
      tmp[1] = '\0';
      if(modifier_index == ALT && isalpha(tmp[0])){
       tmp[2]='\0';
       tmp[1]=tmp[0];
       tmp[0]='\033';
      }
      return tmp;
    }
  return NULL;
}

/* Distinguish 10-key or shifted keypad and enhanced keys. Detect ALT. */
int isSpecialKey(unsigned vk, DWORD cks) {
  if((cks & NUMLOCK_ON)!=0) return 0;  /* keypad is 10-key */
  if((cks & ENHANCED_KEY)!=0) return 1;
  if((cks & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))!=0) return 1;
  if(vk==VK_MULTIPLY || vk==VK_SUBTRACT || vk==VK_ADD) return 1;
  return 0;
}

HANDLE get_theio_handle(void){
static HANDLE h= (HANDLE)-1;
if(h == (HANDLE)-1) h = GetStdHandle(STD_INPUT_HANDLE);
return h;
}

int getchGNU(void){
static  char tmp[60]={0};
  HANDLE h = get_theio_handle();
  int ch;

top:
  if (tmp[0]){
    ch=tmp[0]; strcpy(tmp,tmp+1);
    return ch;
  }

  for (;;)
    {
      DWORD nread;
      INPUT_RECORD input_rec;
      const char *toadd = NULL;

      if (!ReadConsoleInput (h, &input_rec, 1, &nread))
          return -1;            /* seems to be failure */

      /* check the event that occurred */
#define virtual_key_code (input_rec.Event.KeyEvent.wVirtualKeyCode)
#define control_key_state (input_rec.Event.KeyEvent.dwControlKeyState)
#define ich (input_rec.Event.KeyEvent.uChar.AsciiChar)
#define wch (input_rec.Event.KeyEvent.uChar.UnicodeChar)
#define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
#define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)
      switch (input_rec.EventType)
        {
        case KEY_EVENT:

          if (!input_rec.Event.KeyEvent.bKeyDown)
            continue;

          if (wch == 0 || isSpecialKey(virtual_key_code,control_key_state))
            { /* arrow/function/enhanced/alt keys */
              toadd = get_nonascii_key (input_rec, tmp);
              if (!toadd) continue;
              strcpy(tmp,toadd);
              break;
            }
          else
            {
              tmp[0] = ich; tmp[1]=0; toadd=tmp; break;
            }
          break;

        default:
          continue;
        }

      if (toadd) goto top; else break;
    }
  return -1;
}
#undef ich
#undef wch
#undef ALT_PRESSED
#undef CTRL_PRESSED
#undef ich

void main(void){
int n=0;
 printf("cygwin/gnu key banger. Shows emitted characters for a keypress\n");
 printf("Press a key. Quit with q, Q or ctrl-c.\n");
 while(1){
  int x;
  x=getchGNU(); if(isprint(x)) printf("%c",x); else printf("[0x%02x]",x);
  if(++n==10){ printf("\n"); n=0;}
  if(x=='q' | x=='Q') break;
 }
 printf("\n");
}
