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

#if 0
typedef struct _INPUT_RECORD { // ir
    WORD EventType;
    union {
        KEY_EVENT_RECORD KeyEvent;
        MOUSE_EVENT_RECORD MouseEvent;
        WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
        MENU_EVENT_RECORD MenuEvent;
        FOCUS_EVENT_RECORD FocusEvent;
    } Event;
} INPUT_RECORD;
typedef struct _KEY_EVENT_RECORD {
    BOOL bKeyDown;
    WORD wRepeatCount;
    WORD wVirtualKeyCode;
    WORD wVirtualScanCode;
    union {
        WCHAR UnicodeChar;
        CHAR  AsciiChar;
    } uChar;
    DWORD dwControlKeyState;
} KEY_EVENT_RECORD;

typedef struct _MOUSE_EVENT_RECORD {
    COORD dwMousePosition;
    DWORD dwButtonState;
    DWORD dwControlKeyState;
    DWORD dwEventFlags;
} MOUSE_EVENT_RECORD;


#endif

/*
 * dwControlKeyState
 * 
 * Indicates the state of the control keys. This member can be a
 * combination of the following values:
 * 
 * CAPSLOCK_ON        The CAPS LOCK light is on.
 * ENHANCED_KEY       The key is enhanced.
 * LEFT_ALT_PRESSED   The left ALT key is pressed.
 * LEFT_CTRL_PRESSED  The left CTRL key is pressed.
 * NUMLOCK_ON         The NUM LOCK light is on.
 * RIGHT_ALT_PRESSED  The right ALT key is pressed.
 * RIGHT_CTRL_PRESSED The right CTRL key is pressed.
 * SCROLLLOCK_ON      The SCROLL LOCK light is on.
 * SHIFT_PRESSED      The SHIFT key is pressed.
 * 
 * Enhanced keys for the IBM 101/102 keyboards are the INS, DEL, HOME,
 * END, PAGE UP, PAGE DOWN, and direction keys in the clusters to the
 * left of the keypad; and the DIVIDE and ENTER keys in the keypad.
 */

#define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
#define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)
#define IsKey(x) ((keystate & x) != 0)

char keyDescription[256];
char *translate(c,keycode,keystate) int c,keycode; long keystate; {
char buf[256]; char *p;

p=(char *)&keyDescription[0]; p[0]=0;
if(IsKey(ENHANCED_KEY)) strcat(p,"GREY ");
if(IsKey(ALT_PRESSED)) strcat(p,"ALT ");
if(IsKey(CTRL_PRESSED)) strcat(p,"CTRL ");
if(IsKey(CAPSLOCK_ON)){ 
  strcat(p,"CapsLOCK ");
  if(IsKey(SHIFT_PRESSED)) c=tolower(c); else c=toupper(c);
}
if(IsKey(SHIFT_PRESSED)) {
  strcat(p,"SHIFT ");
  if(IsKey(CAPSLOCK_ON)) c=tolower(c); else c=toupper(c);
}
switch(c){
case '+': if(keycode==78) strcat(p,"GREY "); break;
case '-': if(keycode==74) strcat(p,"GREY "); break;
case '*': if(keycode==55) strcat(p,"GREY "); break;
};
if(c != 0) {
  if(isprint(c)) sprintf(buf,"%c",c);
  else sprintf(buf,"0x%04x ",c);
  strcat(p,buf);
} else {
  if(IsKey(CTRL_PRESSED)){
    switch(keycode){
    case 53: c='/'; break;
    case 7:  c='^'; break;
    case 12: c='_'; break;
    };
  }
  if(c != 0){ sprintf(buf,"%c",c); strcat(p,buf);}
}
return p;
};

#if 0

Key = 0000, scan=59, F1
Key = 0000, scan=60, F2
Key = 0000, scan=61, F3
Key = 0000, scan=62, F4
Key = 0000, scan=63, F5
Key = 0000, scan=64, F6
Key = 0000, scan=65, F7
Key = 0000, scan=66, F8
Key = 0000, scan=67, F9
Key = 0000, scan=68, F10      
Key = 0000, scan=87, F11
Key = 0000, scan=88, F12


Key = 0000, scan=82, keystate=10000000, Trans='GREY ' INSERT
Key = 0000, scan=83, keystate=10000000, Trans='GREY ' DELETE
Key = 0000, scan=71, keystate=10000000, Trans='GREY ' HOME
Key = 0000, scan=79, keystate=10000000, Trans='GREY ' END
Key = 0000, scan=73, keystate=10000000, Trans='GREY ' PAGEUP
Key = 0000, scan=81, keystate=10000000, Trans='GREY ' PAGEDOWN


Key = 0000, scan=75, keystate=10000000, Trans='GREY ' LEFT
Key = 0000, scan=77, keystate=10000000, Trans='GREY ' RIGHT
Key = 0000, scan=72, keystate=10000000, Trans='GREY ' UP
Key = 0000, scan=80, keystate=10000000, Trans='GREY ' DOWN

Key = 0000, scan=82, keystate=00000000, Trans=''  INS ZERO
Key = 0000, scan=83, keystate=00000000, Trans=''  DEL PERIOD
Key = 0000, scan=79, keystate=00000000, Trans=''  END
Key = 0000, scan=80, keystate=00000000, Trans=''  DOWN
Key = 0000, scan=81, keystate=00000000, Trans=''  PAGEDOWN
Key = 000d, scan=28, keystate=10000000, Trans='GREY 0x000d ' ENTER
Key = 0000, scan=75, keystate=00000000, Trans=''  LEFT
Key = 0000, scan=76, keystate=00000000, Trans=''  MIDDLE
Key = 0000, scan=77, keystate=00000000, Trans=''  RIGHT
Key = 002b, scan=78, keystate=00000000, Trans='GREY +'  PLUS
Key = 0000, scan=71, keystate=00000000, Trans=''  HOME
Key = 0000, scan=72, keystate=00000000, Trans=''  UP
Key = 0000, scan=73, keystate=00000000, Trans=''  PAGEUP
Key = 0000, scan=69, keystate=00000000, Trans=''  NUMLOCK
Key = 002f, scan=53, keystate=10000000, Trans='GREY /'   DIVIDE
Key = 002a, scan=55, keystate=00000000, Trans='GREY *'   MULTIPLY
Key = 002d, scan=74, keystate=00000000, Trans='GREY -'   MINUS

Key = 001b, scan=1, keystate=00000000, Trans='0x001b '  PRINT SCRN
Key = 0000, scan=70, keystate=00000000, Trans=''        SCROLL LOCK


#endif


struct bufferinfo {int key,scancode; char *str; int key1,key2;};

struct bufferinfo fkeys[]={
/* Function keys, no modifiers */
{0x00+NOMODIFIER, 59, "F1", 0x00, 59}, /* 59 = ';' */     
{0x00+NOMODIFIER, 60, "F2", 0x00, 60},      
{0x00+NOMODIFIER, 61, "F3", 0x00, 61},      
{0x00+NOMODIFIER, 62, "F4", 0x00, 62},      
{0x00+NOMODIFIER, 63, "F5", 0x00, 63},      
{0x00+NOMODIFIER, 64, "F6", 0x00, 64},      
{0x00+NOMODIFIER, 65, "F7", 0x00, 65},      
{0x00+NOMODIFIER, 66, "F8", 0x00, 66},      
{0x00+NOMODIFIER, 67, "F9", 0x00, 67},      
{0x00+NOMODIFIER, 68, "F10", 0x00, 68},     
{0x00+NOMODIFIER, 87, "F11", 0xe0, '.'+128},
{0x00+NOMODIFIER, 88, "F12", 0xe0, '.'+128+1},
{0x00+NOMODIFIER, 82, "GreyINSERT", 0xe0, 'R'},
{0x00+NOMODIFIER, 83, "GreyDELETE", 0xe0, 'S'},
{0x00+NOMODIFIER, 71, "GreyHOME", 0xe0, 'G'},
{0x00+NOMODIFIER, 79, "GreyEND", 0xe0, 'O'},  
{0x00+NOMODIFIER, 73, "GreyPAGEUP", 0xe0, 'I'},
{0x00+NOMODIFIER, 81, "GreyPAGEDOWN", 0xe0, 'Q'},
/* Function keys, Shifted */
{0x00+SHIFTMOD, 59, "F1", 0x00, 84}, /* 84 = 'T' */     
{0x00+SHIFTMOD, 60, "F2", 0x00, 85},      
{0x00+SHIFTMOD, 61, "F3", 0x00, 86},      
{0x00+SHIFTMOD, 62, "F4", 0x00, 87},      
{0x00+SHIFTMOD, 63, "F5", 0x00, 88},      
{0x00+SHIFTMOD, 64, "F6", 0x00, 89},      
{0x00+SHIFTMOD, 65, "F7", 0x00, 90},      
{0x00+SHIFTMOD, 66, "F8", 0x00, 91},      
{0x00+SHIFTMOD, 67, "F9", 0x00, 92},      
{0x00+SHIFTMOD, 68, "F10", 0x00, 93},     
{0x00+SHIFTMOD, 87, "F11", 0xe0, '.'+128+2},
{0x00+SHIFTMOD, 88, "F12", 0xe0, '.'+128+3},
/* Function keys, CtrlKey */
{0x00+CTRLMOD, 59, "F1", 0x00, 94}, /* 94 = '^' */     
{0x00+CTRLMOD, 60, "F2", 0x00, 95},      
{0x00+CTRLMOD, 61, "F3", 0x00, 96},      
{0x00+CTRLMOD, 62, "F4", 0x00, 97},      
{0x00+CTRLMOD, 63, "F5", 0x00, 98},      
{0x00+CTRLMOD, 64, "F6", 0x00, 99},      
{0x00+CTRLMOD, 65, "F7", 0x00, 100},      
{0x00+CTRLMOD, 66, "F8", 0x00, 101},      
{0x00+CTRLMOD, 67, "F9", 0x00, 102},      
{0x00+CTRLMOD, 68, "F10", 0x00, 103},     
{0x00+CTRLMOD, 87, "F11", 0x00, '%'+128},
{0x00+CTRLMOD, 88, "F12", 0x00, '%'+128+1},
/* Function keys, AltKey */
{0x00+ALTMOD, 59, "F1", 0x00, 104}, /* 104 = 'h' */     
{0x00+ALTMOD, 60, "F2", 0x00, 105},      
{0x00+ALTMOD, 61, "F3", 0x00, 106},      
{0x00+ALTMOD, 62, "F4", 0x00, 107},      
{0x00+ALTMOD, 63, "F5", 0x00, 108},      
{0x00+ALTMOD, 64, "F6", 0x00, 109},      
{0x00+ALTMOD, 65, "F7", 0x00, 110},      
{0x00+ALTMOD, 66, "F8", 0x00, 111},      
{0x00+ALTMOD, 67, "F9", 0x00, 112},      
{0x00+ALTMOD, 68, "F10", 0x00, 113},     
{0x00+ALTMOD, 87, "F11", 0x00, '%'+128+2},
{0x00+ALTMOD, 88, "F12", 0x00, '%'+128+3},
/* Grey enhanced keys */
{0x1b+ENHANCEDMOD, 1,  "PrintScrn", 0xe0, 1},
{0x00+ENHANCEDMOD, 70, "ScrollLock", 0xe0, 70},
{0x00+ENHANCEDMOD, 82, "INSERT", 0xe0, 'R'},
{0x00+ENHANCEDMOD, 83, "DELETE", 0xe0, 'S'},
{0x00+ENHANCEDMOD, 71, "HOME", 0xe0, 'G'},
{0x00+ENHANCEDMOD, 79, "END", 0xe0, 'O'},
{0x00+ENHANCEDMOD, 73, "PAGEUP", 0xe0, 'I'},
{0x00+ENHANCEDMOD, 81, "PAGEDOWN", 0xe0, 'Q'},
{0x00+ENHANCEDMOD, 75, "LEFT", 0xe0, 'K'},
{0x00+ENHANCEDMOD, 77, "RIGHT", 0xe0, 'M'},
{0x00+ENHANCEDMOD, 72, "UP", 0xe0, 'H'},
{0x00+ENHANCEDMOD, 80, "DOWN", 0xe0, 'P'},
/* Application keypad */
{0x00+NOMODIFIER, 76, "KeypadMIDDLE", 0xe0, 76},
{0x00+NOMODIFIER, 69, "KeypadNUMLOCK", 0xe0, 69},
{0x2f+ENHANCEDMOD, 53, "KeypadDIVIDE", 0xe0, 53},
{0x2a+NOMODIFIER, 55, "KeypadMULTIPLY", 0xe0, 55},
{0x2d+NOMODIFIER, 74, "KeypadMINUS", 0xe0, 74},
{0x0d+ENHANCEDMOD, 28, "KeypadENTER", 0xe0, 28},
{0x2b+NOMODIFIER, 78, "KeypadPLUS", 0xe0, 78},
{0x00+NOMODIFIER, 82, "KeypadINSERT", 0x00, 82},
{0x00+NOMODIFIER, 83, "KeypadDELETE", 0x00, 83},
{0x00+NOMODIFIER, 71, "KeypadHOME", 0x00, 71},
{0x00+NOMODIFIER, 79, "KeypadEND", 0x00, 79},
{0x00+NOMODIFIER, 73, "KeypadPAGEUP", 0x00, 73},
{0x00+NOMODIFIER, 81, "KeypadPAGEDOWN", 0x00, 81},
{0x00+NOMODIFIER, 75, "KeypadLEFT", 0x00, 75},
{0x00+NOMODIFIER, 77, "KeypadRIGHT", 0x00, 77},
{0x00+NOMODIFIER, 72, "KeypadUP", 0x00, 72},
{0x00+NOMODIFIER, 80, "KeypadDOWN", 0x00, 80},
};
#define bufferinfoSIZE (sizeof(fkeys)/sizeof(fkeys[0]))



char bits[64];
char * binary(keystate) long keystate; {
char *p = (char *)&bits[0];
int i;
keystate = (keystate >> 8);
for(i=0;i<8;++i){
  p[i]= ((1 & keystate) != 0) ? '1' : '0';
  keystate = (keystate >> 1);
  p[8]=0;
}
 return p;
}

void keybanger(){
int c;
HANDLE hStdin,hStdout;
INPUT_RECORD Buffer;
PINPUT_RECORD Buf= &Buffer;
long NumRead;
COORD mp;

    hStdout=GetStdHandle(STD_OUTPUT_HANDLE);
    hStdin=GetStdHandle(STD_INPUT_HANDLE);
    SetConsoleMode(hStdin,0 /*ENABLE_MOUSE_INPUT*/); /* noecho, no ctrl-c */
    /* ReadConsoleInput waits for an event. Ctrl-C is not passed through
       unless ENABLE_PROCESSED_INPUT has been turned off */
    while(1){
    int result,scancode; long keystate; char *p;
      result=ReadConsoleInput(hStdin,Buf,(DWORD)1,(LPDWORD)&NumRead);
      if(result==0) {printf("Got zero\n");continue;}
        if(Buffer.EventType==KEY_EVENT){
          c=Buffer.Event.KeyEvent.uChar.AsciiChar;
          scancode=Buffer.Event.KeyEvent.wVirtualScanCode;
          keystate=Buffer.Event.KeyEvent.dwControlKeyState;
          if(Buffer.Event.KeyEvent.bKeyDown==0){
            printf("Key = %04x, scan=%d, ",c,scancode);
            p=translate(c,scancode,keystate);
            printf("keystate=%s, Trans='%s'\n",binary(keystate),p);
            if(c == 'Q' || c == 'q') break;
            if(c == 3) printf("Use Q or q to quit\n");
          }
      }
      if(Buffer.EventType==MOUSE_EVENT){
        mp=Buffer.Event.MouseEvent.dwMousePosition;
        printf("Mouse event, x=%d, y=%d\n",mp.X,mp.Y);
          }
    }

    printf("All done with keybanger\n");
}

/* Once stdout has been redefined to point to the current console, */
/* then all printf output goes to that console.                    */

void launchconsole(){
int hCrt; FILE *hf; long hStdout;
  FreeConsole();
  AllocConsole();            /* Open a console window */
  hStdout=(long)GetStdHandle(STD_OUTPUT_HANDLE);
  hCrt= _open_osfhandle(hStdout,_O_TEXT); /* Get file descriptor */
  hf= fdopen(hCrt,"w" ); /* Get Ansi-C handle for file descriptor*/
  *stdout = *hf;            /* Change stdout to this one */
  setvbuf( stdout, NULL, _IONBF, 0 ); /* no buffering */
}

int main(void){
#if 1
  (void)launchconsole();
#endif
  (void)keybanger();
  return 0; // C runtime cleans up the file opens
}
