/*---------------------------------------------------------------------------
 * filename - getch.c
 *
 * function(s)
 *        getch   - gets character from console
 *        ungetch - ungets character to console
 *--------------------------------------------------------------------------*/

/*
 *      C/C++ Run Time Library - Version 1.5
 *
 *      Copyright (c) 1991, 1994 by Borland International
 *      All Rights Reserved.
 *
 */

#define INCL_CON
#define INCL_USER
#include <ntbc.h>

#include <conio.h>
#include <stdio.h>      /* EOF definition */

#define KBD_TEXTMODE (ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT)

/* The following two variables are located in kbhit.c. */

extern unsigned char _cFlag;    /* Flag presence of un-gotten char */
extern unsigned char _cChar;    /* The ungotten char               */


/*----------------------------------------------------------------------
 * The following table maps NT virtual keycodes to PC BIOS keyboard
 * values.  For each virtual keycode there are four possible BIOS
 * values: normal, shift, Ctrl, and ALT.  Key combinations that have
 * no BIOS equivalent have a value of -1, and are ignored.  Extended
 * (non-ASCII) key values have bit 8 set to 1 using the EXT macro.
 */

#define EXT(key)    ((key)+0x100)
#define ISEXT(val)  ((val)&0x100)
#define EXTVAL(val) ((val)&0xff)

struct kbd
{
    short keycode;            /* virtual keycode */
    short normal;             /* BIOS keycode - normal */
    short shift;              /* BIOS keycode - Shift- */
    short ctrl;               /* BIOS keycode - Ctrl- */
    short alt;                /* BIOS keycode - Alt- */
} kbdtab [] = 
{
/*    Virtual key   Normal      Shift       Control     Alt */

    { VK_BACK,      0x08,       0x08,       0x7f,       EXT(14)  },
    { VK_TAB,       0x09,       EXT(15),    EXT(148),   EXT(165) },
    { VK_RETURN,    0x0d,       0x0d,       0x0a,       EXT(166) },
    { VK_ESCAPE,    0x1b,       0x1b,       0x1b,       EXT(1)   },
    { VK_SPACE,     0x20,       0x20,       EXT(3),     0x20,    },
    { '0',          '0',        ')',        -1,         EXT(129) },
    { '1',          '1',        '!',        -1,         EXT(120) },
    { '2',          '2',        '@',        EXT(3),     EXT(121) },
    { '3',          '3',        '#',        -1,         EXT(122) },
    { '4',          '4',        '$',        -1,         EXT(123) },
    { '5',          '5',        '%',        -1,         EXT(124) },
    { '6',          '6',        '^',        0x1e,       EXT(125) },
    { '7',          '7',        '&',        -1,         EXT(126) },
    { '8',          '8',        '*',        -1,         EXT(127) },
    { '9',          '9',        '(',        -1,         EXT(128) },

    { 'A',          'a',        'A',        0x01,       EXT(30)  },
    { 'B',          'b',        'B',        0x02,       EXT(48)  },
    { 'C',          'c',        'C',        0x03,       EXT(46)  },
    { 'D',          'd',        'D',        0x04,       EXT(32)  },
    { 'E',          'e',        'E',        0x05,       EXT(18)  },
    { 'F',          'f',        'F',        0x06,       EXT(33)  },
    { 'G',          'g',        'G',        0x07,       EXT(34)  },
    { 'H',          'h',        'H',        0x08,       EXT(35)  },
    { 'I',          'i',        'I',        0x09,       EXT(23)  },
    { 'J',          'j',        'J',        0x0a,       EXT(36)  },
    { 'K',          'k',        'K',        0x0b,       EXT(37)  },
    { 'L',          'l',        'L',        0x0c,       EXT(38)  },
    { 'M',          'm',        'M',        0x0d,       EXT(50)  },
    { 'N',          'n',        'N',        0x0e,       EXT(49)  },
    { 'O',          'o',        'O',        0x0f,       EXT(24)  },
    { 'P',          'p',        'P',        0x10,       EXT(25)  },
    { 'Q',          'q',        'Q',        0x11,       EXT(16)  },
    { 'R',          'r',        'R',        0x12,       EXT(19)  },
    { 'S',          's',        'S',        0x13,       EXT(31)  },
    { 'T',          't',        'T',        0x14,       EXT(20)  },
    { 'U',          'u',        'U',        0x15,       EXT(22)  },
    { 'V',          'v',        'V',        0x16,       EXT(47)  },
    { 'W',          'w',        'W',        0x17,       EXT(17)  },
    { 'X',          'x',        'X',        0x18,       EXT(45)  },
    { 'Y',          'y',        'Y',        0x19,       EXT(21)  },
    { 'Z',          'z',        'Z',        0x1a,       EXT(44)  },
    
    { VK_PRIOR,     EXT(73),    EXT(73),    EXT(132),   EXT(153) },
    { VK_NEXT,      EXT(81),    EXT(81),    EXT(118),   EXT(161) },
    { VK_END,       EXT(79),    EXT(79),    EXT(117),   EXT(159) },
    { VK_HOME,      EXT(71),    EXT(71),    EXT(119),   EXT(151) },
    { VK_LEFT,      EXT(75),    EXT(75),    EXT(115),   EXT(155) },
    { VK_UP,        EXT(72),    EXT(72),    EXT(141),   EXT(152) },
    { VK_RIGHT,     EXT(77),    EXT(77),    EXT(116),   EXT(157) },
    { VK_DOWN,      EXT(80),    EXT(80),    EXT(145),   EXT(160) },
    { VK_INSERT,    EXT(82),    EXT(82),    EXT(146),   EXT(162) },
    { VK_DELETE,    EXT(83),    EXT(83),    EXT(147),   EXT(163) },
    { VK_NUMPAD0,   '0',        EXT(82),    EXT(146),   -1       },
    { VK_NUMPAD1,   '1',        EXT(79),    EXT(117),   -1       },
    { VK_NUMPAD2,   '2',        EXT(80),    EXT(145),   -1       },
    { VK_NUMPAD3,   '3',        EXT(81),    EXT(118),   -1       },
    { VK_NUMPAD4,   '4',        EXT(75),    EXT(115),   -1       },
    { VK_NUMPAD5,   '5',        EXT(76),    EXT(143),   -1       },
    { VK_NUMPAD6,   '6',        EXT(77),    EXT(116),   -1       },
    { VK_NUMPAD7,   '7',        EXT(71),    EXT(119),   -1       },
    { VK_NUMPAD8,   '8',        EXT(72),    EXT(141),   -1       },
    { VK_NUMPAD9,   '9',        EXT(73),    EXT(132),   -1       },
    { VK_MULTIPLY,  '*',        '*',        EXT(150),   EXT(55)  },
    { VK_ADD,       '+',        '+',        EXT(144),   EXT(78)  },
    { VK_SUBTRACT,  '-',        '-',        EXT(142),   EXT(74)  },
    { VK_DECIMAL,   '.',        '.',        EXT(83),    EXT(147) },
    { VK_DIVIDE,    '/',        '/',        EXT(149),   EXT(164) },
    { VK_F1,        EXT(59),    EXT(84),    EXT(94),    EXT(104) },
    { VK_F2,        EXT(60),    EXT(85),    EXT(95),    EXT(105) },
    { VK_F3,        EXT(61),    EXT(86),    EXT(96),    EXT(106) },
    { VK_F4,        EXT(62),    EXT(87),    EXT(97),    EXT(107) },
    { VK_F5,        EXT(63),    EXT(88),    EXT(98),    EXT(108) },
    { VK_F6,        EXT(64),    EXT(89),    EXT(99),    EXT(109) },
    { VK_F7,        EXT(65),    EXT(90),    EXT(100),   EXT(110) },
    { VK_F8,        EXT(66),    EXT(91),    EXT(101),   EXT(111) },
    { VK_F9,        EXT(67),    EXT(92),    EXT(102),   EXT(112) },
    { VK_F10,       EXT(68),    EXT(93),    EXT(103),   EXT(113) },
    { VK_F11,       EXT(133),   EXT(135),   EXT(137),   EXT(139) },
    { VK_F12,       EXT(134),   EXT(136),   EXT(138),   EXT(140) },
    { 0xdc,         '\\',       '|',        0x1c,       EXT(43)  },
    { 0xbf,         '/',        '?',        -1,         EXT(53)  },
    { 0xbd,         '-',        '_',        0x1f,       EXT(130) },
    { 0xbb,         '=',        '+',        -1,         EXT(131) },
    { 0xdb,         '[',        '{',        0x1b,       EXT(26)  },
    { 0xdd,         ']',        '}',        0x1d,       EXT(27)  },
    { 0xba,         ';',        ':',        -1,         EXT(39)  },
    { 0xde,         '\'',       '"',        -1,         EXT(40)  },
    { 0xbc,         ',',        '<',        -1,         EXT(51)  },
    { 0xbe,         '.',        '>',        -1,         EXT(52)  },
    { 0xc0,         '`',        '~',        -1,         EXT(41)  },

    { -1,           -1,         -1,         -1,         -1       }  /** END **/
};
/*--------------------------------------------------------------------------*

Name            getch - gets character from console

Usage           int getch(void);

Prototype in    conio.h

Description     getch is a function that reads a single character directly
                from the console, without echoing.

Return value    getch and getche return the character read. There is no
                error return for these two functions.

*---------------------------------------------------------------------------*/

int _RTLENTRY _EXPFUNC getch(void)
{
    INPUT_RECORD inp;
    DWORD nread;
    HANDLE hin;
    DWORD kbdmode;
    struct kbd *k;
    int keycode, state, c;
    static int extend = -1;

    /* If a previous ungetch() call has been performed, return
     * the key that it saved.
     */
    if (_cFlag)                     /* Previous ungetch? */
    {
        _cFlag = 0;                 /* Reset the flag   */
        return (_cChar & 0xff);     /* Get the un-got char  */
    }

    /* If a previous call returned an extended code, return
     * the scan code for the key.
     */
    if (extend != -1)
    {
        c = extend;
        extend = -1;                /* Reset the flag */
        return c;                   /* Return the extended scancode */
    }

    /* Get the keyboard mode, then set it to binary if necessary.
     * This is done to prevent NT from handling control keys
     * like Control-C and Control-S.
     */
    hin = GetStdHandle(STD_INPUT_HANDLE);
    if (GetConsoleMode(hin, &kbdmode) != TRUE)
        return -1;
    if ((kbdmode & KBD_TEXTMODE) != 0)
        if (SetConsoleMode(hin, kbdmode & ~KBD_TEXTMODE) != TRUE)
            return -1;

    /* Get keyboard events until a recognizable one appears.
     */
    for (;;)
    {
        if (ReadConsoleInput(hin, &inp, 1, &nread) != TRUE)
        {
            c = -1;
            break;
        }
        else if (inp.Event.KeyEvent.bKeyDown)
        {
            keycode = inp.Event.KeyEvent.wVirtualKeyCode;
            state   = inp.Event.KeyEvent.dwControlKeyState;

            /* Look up the virtual keycode in the table.  Ignore
             * unrecognized keys.
             */
            for (k = &kbdtab[0]; keycode != k->keycode && k->keycode != -1; k++)
                ;
            if (k->keycode == -1)       /* value not in table */
                continue;

            /* Check the state of the shift keys.  ALT has highest
             * priority, followed by Control, followed by Shift.
             * Select the appropriate table entry based on shift state.
             */
            if (state & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
                c = k->alt;
            else if (state & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
                c = k->ctrl;
            else if (state & SHIFT_PRESSED)
                c = k->shift;
            else
            {
                /* If it is a letter key, use the ASCII value supplied
                 * by NT to take into account the CapsLock state.
                 */
                if (keycode >= 'A' && keycode <= 'Z')
                    c = inp.Event.KeyEvent.uChar.AsciiChar;
                else
                    c = k->normal;
            }
            if (c == -1)
                continue;               /* no BIOS equivalent */

            /* If it is an extended key, save the key value and
             * return 0.  The next time we're called, we'll return
             * the key value.
             */
            if (ISEXT(c))
            {
                extend = EXTVAL(c);
                c = 0;
            }
            break;
        }
    }

    /* If we changed the keyboard mode to binary mode earlier,
     * change it back to its original mode.
     */
    if ((kbdmode & KBD_TEXTMODE) != 0)
        if (SetConsoleMode(hin, kbdmode) != TRUE)
            return -1;

    return c;
}

/*--------------------------------------------------------------------------*

Name            ungetch - ungets character to console

Usage           int ungetch(int c);

Prototype in    conio.h

Description     ungetch pushes the character c back to the console,
                causing c to be the next character read. The ungetch
                function fails if it is called more than once before
                the next read.

Return value    ungetch returns the character c if it is successful.
                return value of EOF indicates an error.

*---------------------------------------------------------------------------*/

int _RTLENTRY _EXPFUNC ungetch(int c)
{
    if (_cFlag) 
        return(EOF);

    _cFlag = 1;
    return ((_cChar = c) & 0xff);
}
