//  This may look like C code, but it is really -*- C++ -*-

//  ------------------------------------------------------------------
//  The Goldware Library
//  Copyright (C) 1990-1999 Odinn Sorensen
//  Copyright (C) 1999-2000 Alexander S. Aganichev
//  Copyright (C) 2000 Jacobo Tarrio
//  ------------------------------------------------------------------
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Library General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Library General Public License for more details.
//
//  You should have received a copy of the GNU Library General Public
//  License along with this program; if not, write to the Free
//  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
//  MA 02111-1307, USA
//  ------------------------------------------------------------------
//  $Id: gkbdbase.cpp,v 1.1 2011/02/18 19:46:01 stas_degteff Exp $
//  ------------------------------------------------------------------
//  GCUI: Golded+ Character-oriented User Interface.
//  Keyboard functions.
//  ------------------------------------------------------------------

#include <gctype.h>
#include <gmemdbg.h>
#include <gkbdcode.h>
#include <gkbdbase.h>
#include <gmemall.h>

#include <stdlib.h>

#if defined(__OS2__)
#define INCL_BASE
#include <os2.h>
#endif

#ifdef __WIN32__
#include <windows.h>
extern OSVERSIONINFO WinVer;
#endif

#if defined(__UNIX__) && !defined(__USE_NCURSES__)
#include <gkbdunix.h>
#endif

#if defined(__DJGPP__)
#include <sys/farptr.h>
#endif

#if defined(__USE_NCURSES__)
#include <gcurses.h>
#endif

#if defined(__linux__)
#include <sys/ioctl.h>
#include <stdio.h>
#endif

#if defined(__BEOS__)
#include <InterfaceDefs.h>
#endif

//  ------------------------------------------------------------------

#if defined(__USE_NCURSES__)
int curses_initialized = 0;
#endif


//  ------------------------------------------------------------------

#if defined(__WIN32__)
#define KBD_TEXTMODE (ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT)
#endif


//  ------------------------------------------------------------------
//  Global keyboard data

#if defined(__WIN32__) && !defined(__USE_NCURSES__)
HANDLE gkbd_hin;
DWORD  gkbd_kbdmode;
int    gkbd_nt;
#endif

GKbd gkbd;

int blanked = false;

bool right_alt_same_as_left = false;

//  ------------------------------------------------------------------
//  Keyboard Class Initializer

void GKbd::Init() {

  #if defined(__USE_NCURSES__)

  // Both screen and keyboard must be initialized at once
  if(0 == (curses_initialized++)) {
    initscr();
    raw();
    noecho();
    nonl();
    intrflush(stdscr, FALSE);
    keypad(stdscr, TRUE);
  }

  // WARNING: this might break with an old version of ncurses, or
  // with another implementation of curses. I'm putting it here because
  // it is quote useful most of the time :-) For other implementations of
  // curses, you might have to compile curses yourself to achieve this.  -jt
  #if defined(NCURSES_VERSION)
  if(not getenv("ESCDELAY")) // If not specified by user via environment, set
    ESCDELAY = 50; // ms, slow for a 300bps terminal, fast for humans :-)
  #endif
  // For more ncurses-dependent code, look at the gkbd_curstable array
  // and at the kbxget_raw() function  -jt

  #elif defined(__OS2__)

  KBDINFO kbstInfo;
  kbstInfo.cb = sizeof(kbstInfo);
  KbdGetStatus(&kbstInfo, 0);
  kbstInfo.fsMask = (USHORT)((kbstInfo.fsMask & 0xFFF7) | 0x0004);
  KbdSetStatus(&kbstInfo, 0);

  #elif defined(__WIN32__)

  OSVERSIONINFO osversion;
  osversion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  GetVersionEx(&osversion);
  gkbd_nt = make_bool(osversion.dwPlatformId & VER_PLATFORM_WIN32_NT);
  gkbd_hin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                         OPEN_EXISTING, 0, NULL);
  GetConsoleMode(gkbd_hin, &gkbd_kbdmode);
  if(gkbd_kbdmode & KBD_TEXTMODE)
    SetConsoleMode(gkbd_hin, gkbd_kbdmode & ~KBD_TEXTMODE);

  #elif defined(__UNIX__)

  gkbd_tty_init();

  #endif
}


//  ------------------------------------------------------------------
//  Keyboard Class constructor

GKbd::GKbd() {

  kbuf = NULL;
  onkey  = NULL;
  curronkey = NULL;
  inmenu = 0;
  source = 0;
  polling = 0;
  tickinterval = 0;
  tickpress = tickvalue = gclock();
  tickfunc = NULL;
  inidle = 0;
  quitall = NO;

  // Detect enhanced keyboard by checking bit 4 at 0x00000496
  #if defined(__USE_NCURSES__)
  extkbd = true;
  #elif defined(__DJGPP__)
  extkbd = _farpeekb (_dos_ds, 0x0496) & (1 << 4);
  #elif defined(__MSDOS__)
  extkbd = *((byte*)0x0496) & (1 << 4);
  #elif defined(__OS2__) || defined(__WIN32__)
  extkbd = true;
  #endif

  Init();

  #if defined(__UNIX__) && !defined(__USE_NCURSES__) && !defined(__BEOS__)

  gkbd_keymap_init();

  char escseq[2];
  escseq[1] = NUL;
  for(int n=0; n<256; n++) {
    escseq[0] = (char)n;
    if(n == 0x7F or n == 0x08)
      gkbd_define_keysym(escseq, Key_BS);
    else if(n == 0x09)
      gkbd_define_keysym(escseq, Key_Tab);
    else if(n == 0x0D)
      gkbd_define_keysym(escseq, Key_Ent);
    else
      gkbd_define_keysym(escseq, (n < 128) ? (scancode_table[n]|n) : n);
  }

  gkbd_define_keysym("^@", 0);

  gkbd_define_keysym("\033[A", Key_Up);
  gkbd_define_keysym("\033[B", Key_Dwn);
  gkbd_define_keysym("\033[C", Key_Rgt);
  gkbd_define_keysym("\033[D", Key_Lft);

  gkbd_define_keysym("\033[[W", Key_C_Up);
  gkbd_define_keysym("\033[[Z", Key_C_Dwn);
  gkbd_define_keysym("\033[[Y", Key_C_Rgt);
  gkbd_define_keysym("\033[[X", Key_C_Lft);

  gkbd_define_keysym("\033[1~", Key_Home);
  gkbd_define_keysym("\033[7~", Key_Home);
  gkbd_define_keysym("\033[H",  Key_Home);
  gkbd_define_keysym("\033[2~", Key_Ins);
  gkbd_define_keysym("\033[3~", Key_Del);
  gkbd_define_keysym("\033[4~", Key_End);
  gkbd_define_keysym("\033[8~", Key_End);
  gkbd_define_keysym("\033[F",  Key_End);
  gkbd_define_keysym("\033[5~", Key_PgUp);
  gkbd_define_keysym("\033[6~", Key_PgDn);

  gkbd_define_keysym("\033[[A", Key_F1);
  gkbd_define_keysym("\033[[B", Key_F2);
  gkbd_define_keysym("\033[[C", Key_F3);
  gkbd_define_keysym("\033[[D", Key_F4);
  gkbd_define_keysym("\033[[E", Key_F5);
  gkbd_define_keysym("\033[17~", Key_F6);
  gkbd_define_keysym("\033[18~", Key_F7);
  gkbd_define_keysym("\033[19~", Key_F8);
  gkbd_define_keysym("\033[20~", Key_F9);
  gkbd_define_keysym("\033[21~", Key_F10);

  gkbd_define_keysym("\033[23~", Key_S_F1);
  gkbd_define_keysym("\033[24~", Key_S_F2);
  gkbd_define_keysym("\033[25~", Key_S_F3);
  gkbd_define_keysym("\033[26~", Key_S_F4);
  gkbd_define_keysym("\033[28~", Key_S_F5);
  gkbd_define_keysym("\033[29~", Key_S_F6);
  gkbd_define_keysym("\033[31~", Key_S_F7);
  gkbd_define_keysym("\033[32~", Key_S_F8);
  gkbd_define_keysym("\033[33~", Key_S_F9);
  gkbd_define_keysym("\033[34~", Key_S_F10);

  gkbd_define_keysym("\033""0", Key_A_0);
  gkbd_define_keysym("\033""1", Key_A_1);
  gkbd_define_keysym("\033""2", Key_A_2);
  gkbd_define_keysym("\033""3", Key_A_3);
  gkbd_define_keysym("\033""4", Key_A_4);
  gkbd_define_keysym("\033""5", Key_A_5);
  gkbd_define_keysym("\033""6", Key_A_6);
  gkbd_define_keysym("\033""7", Key_A_7);
  gkbd_define_keysym("\033""8", Key_A_8);
  gkbd_define_keysym("\033""9", Key_A_9);

  gkbd_define_keysym("\033a", Key_A_A);
  gkbd_define_keysym("\033b", Key_A_B);
  gkbd_define_keysym("\033c", Key_A_C);
  gkbd_define_keysym("\033d", Key_A_D);
  gkbd_define_keysym("\033e", Key_A_E);
  gkbd_define_keysym("\033f", Key_A_F);
  gkbd_define_keysym("\033g", Key_A_G);
  gkbd_define_keysym("\033h", Key_A_H);
  gkbd_define_keysym("\033i", Key_A_I);
  gkbd_define_keysym("\033j", Key_A_J);
  gkbd_define_keysym("\033k", Key_A_K);
  gkbd_define_keysym("\033l", Key_A_L);
  gkbd_define_keysym("\033m", Key_A_M);
  gkbd_define_keysym("\033n", Key_A_N);
  gkbd_define_keysym("\033o", Key_A_O);
  gkbd_define_keysym("\033p", Key_A_P);
  gkbd_define_keysym("\033q", Key_A_Q);
  gkbd_define_keysym("\033r", Key_A_R);
  gkbd_define_keysym("\033s", Key_A_S);
  gkbd_define_keysym("\033t", Key_A_T);
  gkbd_define_keysym("\033u", Key_A_U);
  gkbd_define_keysym("\033v", Key_A_V);
  gkbd_define_keysym("\033w", Key_A_W);
  gkbd_define_keysym("\033x", Key_A_X);
  gkbd_define_keysym("\033y", Key_A_Y);
  gkbd_define_keysym("\033z", Key_A_Z);

  gkbd_define_keysym("^?", Key_BS);
  gkbd_define_keysym("\033\x7F", Key_A_BS);
  gkbd_define_keysym("\033\x0D", Key_A_Ent);
  gkbd_define_keysym("\033\x09", Key_A_Tab);

  #elif defined(__BEOS__)

  gkbd_keymap_init();

  char escseq[2];
  escseq[1] = NUL;
  for(int n=0; n<256; n++) {
    escseq[0] = (char)n;
    if(n == 0x08)
      gkbd_define_keysym(escseq, Key_BS);
    else if(n == 0x09)
      gkbd_define_keysym(escseq, Key_Tab);
    else if(n == 0x0D)
      gkbd_define_keysym(escseq, Key_Ent);
    else if(n == 0x7F)
      gkbd_define_keysym(escseq, Key_Del); // ?????
    else
      gkbd_define_keysym(escseq, (n < 128) ? (scancode_table[n]|n) : n);
  }

  // gkbd_define_keysym("^@", 0); ?????????

  gkbd_define_keysym("\033[A", Key_Up);
  gkbd_define_keysym("\033[B", Key_Dwn);
  gkbd_define_keysym("\033[C", Key_Rgt);
  gkbd_define_keysym("\033[D", Key_Lft);

  gkbd_define_keysym("\033[1~", Key_Home);
  gkbd_define_keysym("\033[2~", Key_Ins);
  gkbd_define_keysym("\033[4~", Key_End);
  gkbd_define_keysym("\033[5~", Key_PgUp);
  gkbd_define_keysym("\033[6~", Key_PgDn);

  // gkbd_define_keysym("\033[3~", Key_Del);

  gkbd_define_keysym("\033[11~", Key_F1);
  gkbd_define_keysym("\033[12~", Key_F2);
  gkbd_define_keysym("\033[13~", Key_F3);
  gkbd_define_keysym("\033[14~", Key_F4);
  gkbd_define_keysym("\033[15~", Key_F5);
  gkbd_define_keysym("\033[16~", Key_F6);
  gkbd_define_keysym("\033[17~", Key_F7);
  gkbd_define_keysym("\033[18~", Key_F8);
  gkbd_define_keysym("\033[19~", Key_F9);
  gkbd_define_keysym("\033[20~", Key_F10);

/*
  gkbd_define_keysym("\033\x7F", Key_A_BS);
  gkbd_define_keysym("\033\x0D", Key_A_Ent);
  gkbd_define_keysym("\033\x09", Key_A_Tab);
*/

  #endif
}


//  ------------------------------------------------------------------
//  Keyboard Class destructor

GKbd::~GKbd() {

  #if defined(__USE_NCURSES__)

  if(0 == (--curses_initialized))
    endwin();

  #elif defined(__WIN32__)

  if(gkbd_kbdmode & KBD_TEXTMODE)
    SetConsoleMode(gkbd_hin, gkbd_kbdmode);

  #elif defined(__UNIX__)

  gkbd_keymap_reset();
  gkbd_tty_reset();

  #endif
}


//  ------------------------------------------------------------------
//  Local table for scancode()

gkey scancode_table[] = {

  Key_C_2   & 0xFF00u,     //  0x0300          C <2 @>     [NUL]
  Key_C_A   & 0xFF00u,     //  0x1E01          C <A>       [SOH]
  Key_C_B   & 0xFF00u,     //  0x3002          C <B>       [STX]
  Key_C_C   & 0xFF00u,     //  0x2E03          C <C>       [ETX]
  Key_C_D   & 0xFF00u,     //  0x2004          C <D>       [EOT]
  Key_C_E   & 0xFF00u,     //  0x1205          C <E>       [ENQ]
  Key_C_F   & 0xFF00u,     //  0x2106          C <F>       [ACK]
  Key_C_G   & 0xFF00u,     //  0x2207          C <G>       [BEL]
  Key_C_H   & 0xFF00u,     //  0x2308          C <H>       [BS]
  Key_C_I   & 0xFF00u,     //  0x1709          C <I>       [HT]
  Key_C_J   & 0xFF00u,     //  0x240A          C <J>       [LF]
  Key_C_K   & 0xFF00u,     //  0x250B          C <K>       [VT]
  Key_C_L   & 0xFF00u,     //  0x260C          C <L>       [FF]
  Key_C_M   & 0xFF00u,     //  0x320D          C <M>       [CR]
  Key_C_N   & 0xFF00u,     //  0x310E          C <N>       [SO]
  Key_C_O   & 0xFF00u,     //  0x180F          C <O>       [SI]
  Key_C_P   & 0xFF00u,     //  0x1910          C <P>       [DLE]
  Key_C_Q   & 0xFF00u,     //  0x1011          C <Q>       [DC1]
  Key_C_R   & 0xFF00u,     //  0x1312          C <R>       [DC2]
  Key_C_S   & 0xFF00u,     //  0x1F13          C <S>       [DC3]
  Key_C_T   & 0xFF00u,     //  0x1414          C <T>       [DC4]
  Key_C_U   & 0xFF00u,     //  0x1615          C <U>       [NAK]
  Key_C_V   & 0xFF00u,     //  0x2F16          C <V>       [SYN]
  Key_C_W   & 0xFF00u,     //  0x1117          C <W>       [ETB]
  Key_C_X   & 0xFF00u,     //  0x2D18          C <X>       [CAN]
  Key_C_Y   & 0xFF00u,     //  0x1519          C <Y>       [EM]
  Key_C_Z   & 0xFF00u,     //  0x2C1A          C <Z>       [SUB]
  Key_Esc   & 0xFF00u,     //  0x011B          C <[ {>     [ESC] (was: 0x1A1B)
  Key_C_Bsl & 0xFF00u,     //  0x2B1C          C <\ |>     [FS]
  Key_C_Rbr & 0xFF00u,     //  0x1B1D          C <] }>     [GS]
  Key_C_6   & 0xFF00u,     //  0x071E          C <7 &>     [RS]
  Key_C_Min & 0xFF00u,     //  0x0C1F          C <- _>
  Key_Space & 0xFF00u,     //  0x3920            <Space>
  Key_S_1   & 0xFF00u,     //  0x0221          <1 !>
  Key_S_Quo & 0xFF00u,     //  0x2822          <' ">
  Key_S_3   & 0xFF00u,     //  0x0423          <3 #>
  Key_S_4   & 0xFF00u,     //  0x0524          <4 $>
  Key_S_5   & 0xFF00u,     //  0x0625          <5 %>
  Key_S_7   & 0xFF00u,     //  0x0826          <7 &>
  Key_Quo   & 0xFF00u,     //  0x2827          <'>
  Key_S_9   & 0xFF00u,     //  0x0A28          <9 (>
  Key_S_0   & 0xFF00u,     //  0x0B29          <0 )>
  Key_S_8   & 0xFF00u,     //  0x092A          <8 *>
  Key_S_Equ & 0xFF00u,     //  0x0D2B          <= +>
  Key_Com   & 0xFF00u,     //  0x332C            <,>
  Key_Min   & 0xFF00u,     //  0x0C2D          <->
  Key_Dot   & 0xFF00u,     //  0x342E            <.>
  Key_Sls   & 0xFF00u,     //  0x352F          </>
  Key_0     & 0xFF00u,     //  0x0B30          <0>
  Key_1     & 0xFF00u,     //  0x0231          <1>
  Key_2     & 0xFF00u,     //  0x0332          <2>
  Key_3     & 0xFF00u,     //  0x0433          <3>
  Key_4     & 0xFF00u,     //  0x0534          <4>
  Key_5     & 0xFF00u,     //  0x0635          <5>
  Key_6     & 0xFF00u,     //  0x0736          <6>
  Key_7     & 0xFF00u,     //  0x0837          <7>
  Key_8     & 0xFF00u,     //  0x0938          <8>
  Key_9     & 0xFF00u,     //  0x0A39          <9>
  Key_S_Smi & 0xFF00u,     //  0x273A          <; :>
  Key_Smi   & 0xFF00u,     //  0x273B          <;>
  Key_S_Com & 0xFF00u,     //  0x333C          <, >>
  Key_Equ   & 0xFF00u,     //  0x0D3D          <=>
  Key_S_Dot & 0xFF00u,     //  0x343E          <. <>
  Key_S_Sls & 0xFF00u,     //  0x353F          </ ?>
  Key_S_2   & 0xFF00u,     //  0x0340          <2 @>
  Key_S_A   & 0xFF00u,     //  0x1E41          <A>
  Key_S_B   & 0xFF00u,     //  0x3042          <B>
  Key_S_C   & 0xFF00u,     //  0x2E43          <C>
  Key_S_D   & 0xFF00u,     //  0x2044          <D>
  Key_S_E   & 0xFF00u,     //  0x1245          <E>
  Key_S_F   & 0xFF00u,     //  0x2146          <F>
  Key_S_G   & 0xFF00u,     //  0x2247          <G>
  Key_S_H   & 0xFF00u,     //  0x2348          <H>
  Key_S_I   & 0xFF00u,     //  0x1749          <I>
  Key_S_J   & 0xFF00u,     //  0x244A          <J>
  Key_S_K   & 0xFF00u,     //  0x254B          <K>
  Key_S_L   & 0xFF00u,     //  0x264C          <L>
  Key_S_M   & 0xFF00u,     //  0x324D          <M>
  Key_S_N   & 0xFF00u,     //  0x314E          <N>
  Key_S_O   & 0xFF00u,     //  0x184F          <O>
  Key_S_P   & 0xFF00u,     //  0x1950          <P>
  Key_S_Q   & 0xFF00u,     //  0x1051          <Q>
  Key_S_R   & 0xFF00u,     //  0x1352          <R>
  Key_S_S   & 0xFF00u,     //  0x1F53          <S>
  Key_S_T   & 0xFF00u,     //  0x1454          <T>
  Key_S_U   & 0xFF00u,     //  0x1655          <U>
  Key_S_V   & 0xFF00u,     //  0x2F56          <V>
  Key_S_W   & 0xFF00u,     //  0x1157          <W>
  Key_S_X   & 0xFF00u,     //  0x2D58          <X>
  Key_S_Y   & 0xFF00u,     //  0x1559          <Y>
  Key_S_Z   & 0xFF00u,     //  0x2C5A          <Z>
  Key_Lbr   & 0xFF00u,     //  0x1A5B          <[>
  Key_Bsl   & 0xFF00u,     //  0x2B5C          <\>
  Key_Rbr   & 0xFF00u,     //  0x1B5D          <]>
  Key_S_6   & 0xFF00u,     //  0x075E          <6 ^>
  Key_S_Min & 0xFF00u,     //  0x0C5F          <- _>
  Key_Grv   & 0xFF00u,     //  0x2960          <`>
  Key_A     & 0xFF00u,     //  0x1E61          <a>
  Key_B     & 0xFF00u,     //  0x3062          <b>
  Key_C     & 0xFF00u,     //  0x2E63          <c>
  Key_D     & 0xFF00u,     //  0x2064          <d>
  Key_E     & 0xFF00u,     //  0x1265          <e>
  Key_F     & 0xFF00u,     //  0x2166          <f>
  Key_G     & 0xFF00u,     //  0x2267          <g>
  Key_H     & 0xFF00u,     //  0x2368          <h>
  Key_I     & 0xFF00u,     //  0x1769          <i>
  Key_J     & 0xFF00u,     //  0x246A          <j>
  Key_K     & 0xFF00u,     //  0x256B          <k>
  Key_L     & 0xFF00u,     //  0x266C          <l>
  Key_M     & 0xFF00u,     //  0x326D          <m>
  Key_N     & 0xFF00u,     //  0x316E          <n>
  Key_O     & 0xFF00u,     //  0x186F          <o>
  Key_P     & 0xFF00u,     //  0x1970          <p>
  Key_Q     & 0xFF00u,     //  0x1071          <q>
  Key_R     & 0xFF00u,     //  0x1372          <r>
  Key_S     & 0xFF00u,     //  0x1F73          <s>
  Key_T     & 0xFF00u,     //  0x1474          <t>
  Key_U     & 0xFF00u,     //  0x1675          <u>
  Key_V     & 0xFF00u,     //  0x2F76          <v>
  Key_W     & 0xFF00u,     //  0x1177          <w>
  Key_X     & 0xFF00u,     //  0x2D78          <x>
  Key_Y     & 0xFF00u,     //  0x1579          <y>
  Key_Z     & 0xFF00u,     //  0x2C7A          <z>
  Key_S_Lbr & 0xFF00u,     //  0x1A7B          <[ {>
  Key_S_Bsl & 0xFF00u,     //  0x2B7C          <\ |>
  Key_S_Rbr & 0xFF00u,     //  0x1B7D          <] }>
  Key_S_Grv & 0xFF00u,     //  0x297E          <` ~>
  Key_C_BS  & 0xFF00u      //  0x0E7F          C <BS>      [RUB]
};


//  ------------------------------------------------------------------
//  Returns the scan code of an ASCII character

byte scancode(gkey ch) {

  if(KCodAsc(ch) <= 127)
    return (byte)(scancode_table[KCodAsc(ch)] >> 8);
  return 0;
}


//  ------------------------------------------------------------------
//  Translate scancode for ASCII keys

gkey keyscanxlat(gkey k) {

  // Only translate ASCII keys
  if(KCodAsc(k)) {

    // Check for certain ctrl-keys
    switch(KCodAsc(k)) {

      case 0x08:  // CtrlH or BackSpace                     23/0E
        if(k == Key_BS)
          return k;
        else
          break;

      case 0x09:  // CtrlI or Tab                           17/0F
        if(k == Key_Tab)
          return k;
        else
          break;

      case 0x0A:  // CtrlJ or CtrlEnter or GreyCtrlEnter    24/1C/E0
      case 0x0D:  // CtrlM or Enter or GreyEnter            32/1C/E0
        // First, translate Numpad-Enter to main Enter...
        if(k == Key_EntG)
          k = Key_Ent;
        else if(k == Key_C_EntG)
          k = Key_C_Ent;
        else if(k == Key_A_EntG)
          k = Key_A_Ent;
        // ...and now return if main Enter
        if((k == Key_Ent) or (k == Key_C_Ent) or (k == Key_A_Ent))
          return k;
        else
          break;

      case 0x1B:  // Ctrl[ or Esc                           1A/01
        if(k == Key_Esc)
          return k;
        else
          break;

      // asa: Not sure that the following case is required:
      // Key_S_3 == 0x0423, Key_C_U == 0x1615
      case 0x15: // CtrlU or Shift3 (on german keyboards)   16/04
        if(KCodScn(k) == 0x04)
          return k;
        break;

      case 0xE0: // Check for extended key and fix it if necessary
        if(KCodScn(k)) {
          KCodAsc(k) = 0x00;
          return k;
        }
        break;
    }

    // Translate scancode of ASCII key to a known value
    if (KCodAsc(k) <= 127)
      return (gkey)(scancode_table[KCodAsc(k)] | KCodAsc(k));
    else
      return (gkey)(KCodAsc(k));
  }

  return k;
}


//  ------------------------------------------------------------------
//  The following tables map curses keyboard codes to BIOS keyboard
//  values.

#if defined(__USE_NCURSES__)

// This might not work with something other than ncurses... :-(
// If you ever port it to other curses implementation, remember
// that it might have to be changed to another data structure, or
// the array might have to be filled in another manner...

int gkbd_curstable[] = {
  Key_C_Brk, //  KEY_BREAK
  Key_Dwn,   //  KEY_DOWN
  Key_Up,    //  KEY_UP
  Key_Lft,   //  KEY_LEFT
  Key_Rgt,   //  KEY_RIGHT
  Key_Home,  //  KEY_HOME
  Key_BS,    //  KEY_BACKSPACE
  -1,        //  KEY_F0
  Key_F1,    //  KEY_F(1)
  Key_F2,    //  KEY_F(2)
  Key_F3,    //  KEY_F(3)
  Key_F4,    //  KEY_F(4)
  Key_F5,    //  KEY_F(5)
  Key_F6,    //  KEY_F(6)
  Key_F7,    //  KEY_F(7)
  Key_F8,    //  KEY_F(8)
  Key_F9,    //  KEY_F(9)
  Key_F10,   //  KEY_F(10)
  Key_F11,   //  KEY_F(11)
  Key_F12,   //  KEY_F(12)
  Key_S_F3,  //  KEY_F(13)
  Key_S_F4,  //  KEY_F(14)
  Key_S_F5,  //  KEY_F(15)
  Key_S_F6,  //  KEY_F(16)
  Key_S_F7,  //  KEY_F(17)
  Key_S_F8,  //  KEY_F(18)
  Key_S_F9,  //  KEY_F(19)
  Key_S_F10, //  KEY_F(20)
  Key_S_F11, //  KEY_F(21)
  Key_S_F12, //  KEY_F(22)
  -1,        //  KEY_F(23)
  -1,        //  KEY_F(24)
  -1,        //  KEY_F(25)
  -1,        //  KEY_F(26)
  -1,        //  KEY_F(27)
  -1,        //  KEY_F(28)
  -1,        //  KEY_F(29)
  -1,        //  KEY_F(30)
  -1,        //  KEY_F(31)
  -1,        //  KEY_F(32)
  -1,        //  KEY_F(33)
  -1,        //  KEY_F(34)
  -1,        //  KEY_F(35)
  -1,        //  KEY_F(36)
  -1,        //  KEY_F(37)
  -1,        //  KEY_F(38)
  -1,        //  KEY_F(39)
  -1,        //  KEY_F(40)
  -1,        //  KEY_F(41)
  -1,        //  KEY_F(42)
  -1,        //  KEY_F(43)
  -1,        //  KEY_F(44)
  -1,        //  KEY_F(45)
  -1,        //  KEY_F(46)
  -1,        //  KEY_F(47)
  -1,        //  KEY_F(48)
  -1,        //  KEY_F(49)
  -1,        //  KEY_F(50)
  -1,        //  KEY_F(51)
  -1,        //  KEY_F(52)
  -1,        //  KEY_F(53)
  -1,        //  KEY_F(54)
  -1,        //  KEY_F(55)
  -1,        //  KEY_F(56)
  -1,        //  KEY_F(57)
  -1,        //  KEY_F(58)
  -1,        //  KEY_F(59)
  -1,        //  KEY_F(60)
  -1,        //  KEY_F(61)
  -1,        //  KEY_F(62)
  -1,        //  KEY_F(63)
  -1,        //  KEY_DL
  -1,        //  KEY_IL
  Key_Del,   //  KEY_DC
  Key_Ins,   //  KEY_IC
  Key_Ins,   //  KEY_EIC
  -1,        //  KEY_CLEAR
  -1,        //  KEY_EOS
  -1,        //  KEY_EOL
  -1,        //  KEY_SF
  -1,        //  KEY_SR
  Key_PgDn,  //  KEY_NPAGE
  Key_PgUp,  //  KEY_PPAGE
  Key_Tab,   //  KEY_STAB
  -1,        //  KEY_CTAB
  -1,        //  KEY_CATAB
  Key_Ent,   //  KEY_ENTER
  -1,        //  KEY_SRESET
  -1,        //  KEY_RESET
  -1,        //  KEY_PRINT
  Key_End,   //  KEY_LL (hmm... this should be lower left)
  Key_Home,  //  KEY_A1
  Key_PgUp,  //  KEY_A3
  Key_5Num,  //  KEY_B2
  Key_End,   //  KEY_C1
  Key_PgDn,  //  KEY_C3
  Key_S_Tab, //  KEY_BTAB
  Key_Home,  //  KEY_BEG
  -1,        //  KEY_CANCEL
  -1,        //  KEY_CLOSE
  -1,        //  KEY_COMMAND
  -1,        //  KEY_COPY
  -1,        //  KEY_CREATE
  Key_End,   //  KEY_END
  -1,        //  KEY_EXIT
  -1,        //  KEY_FIND
  -1,        //  KEY_HELP
  -1,        //  KEY_MARK
  -1,        //  KEY_MESSAGE
  -1,        //  KEY_MOVE
  -1,        //  KEY_NEXT
  -1,        //  KEY_OPEN
  -1,        //  KEY_OPTIONS
  -1,        //  KEY_PREVIOUS
  -1,        //  KEY_REDO
  -1,        //  KEY_REFERENCE
  -1,        //  KEY_REFRESH
  -1,        //  KEY_REPLACE
  -1,        //  KEY_RESTART
  -1,        //  KEY_RESUME
  -1,        //  KEY_SAVE
  Key_S_Home,//  KEY_SBEG
  -1,        //  KEY_SCANCEL
  -1,        //  KEY_SCOMMAND
  -1,        //  KEY_SCOPY
  -1,        //  KEY_SCREATE
  Key_S_Del, //  KEY_SDC
  -1,        //  KEY_SDL
  -1,        //  KEY_SELECT
  Key_S_End, //  KEY_SEND
  -1,        //  KEY_SEOL
  -1,        //  KEY_SEXIT
  -1,        //  KEY_SFIND
  -1,        //  KEY_SHELP
  Key_S_Home,//  KEY_SHOME
  Key_S_Ins, //  KEY_SIC
  Key_S_Lft, //  KEY_SLEFT
  -1,        //  KEY_SMESSAGE
  -1,        //  KEY_SMOVE
  -1,        //  KEY_SNEXT
  -1,        //  KEY_SOPTIONS
  -1,        //  KEY_SPREVIOUS
  -1,        //  KEY_SPRINT
  -1,        //  KEY_SREDO
  -1,        //  KEY_SREPLACE
  Key_S_Rgt, //  KEY_SRIGHT
  -1,        //  KEY_SRSUME
  -1,        //  KEY_SSAVE
  -1,        //  KEY_SSUSPEND
  -1,        //  KEY_SUNDO
  -1,        //  KEY_SUSPEND
  -1,        //  KEY_UNDO
  -1,        //  KEY_MOUSE
  -1         //  KEY_RESIZE
};

int gkbd_cursgetch(int mode) {

  int key;
#ifndef BUGGY_NCURSES
  nodelay(stdscr, mode);
#else
  wtimeout(stdscr, mode ? 0 : -1);
#endif
  key = getch();
#ifndef BUGGY_NCURSES
  nodelay(stdscr, FALSE);
#else
  wtimeout(stdscr, -1);
#endif

  return key;
}


//  ------------------------------------------------------------------
//  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.

#elif defined(__WIN32__)

#define EXT(key)    ((key)|0x10000)
#define ISEXT(val)  ((val)&0x10000)
#define EXTVAL(val) ((val)&0xFF)

struct kbd {
  int keycode;            // virtual keycode
  int normal;             // BIOS keycode - normal
  int shift;              // BIOS keycode - Shift-
  int ctrl;               // BIOS keycode - Ctrl-
  int alt;                // BIOS keycode - Alt-
} __gkbd_nt2b_table [] =
{

//  ------------------------------------------------------------------
//  Virtual key   Normal      Shift       Control     Alt

  { VK_BACK,      Key_BS,     Key_BS,     Key_C_BS,   Key_A_BS },
  { VK_TAB,       Key_Tab,    Key_S_Tab,  Key_C_Tab,  Key_A_Tab },
  { VK_RETURN,    Key_Ent,    Key_Ent,    Key_C_Ent,  Key_A_Ent },
  { VK_ESCAPE,    Key_Esc,    Key_Esc,    Key_Esc,    Key_A_Esc },
  { VK_SPACE,     -1,         -1,         Key_Space,  Key_Space },
  { VK_APPS,      Key_S_F10,  Key_S_F10,  Key_S_F10,  -1        },

  { '0',          Key_0,      Key_S_0,    -1,         Key_A_0 },
  { '1',          Key_1,      Key_S_1,    -1,         Key_A_1 },
  { '2',          Key_2,      Key_S_2,    Key_C_2,    Key_A_2 },
  { '3',          Key_3,      Key_S_3,    -1,         Key_A_3 },
  { '4',          Key_4,      Key_S_4,    -1,         Key_A_4 },
  { '5',          Key_5,      Key_S_5,    -1,         Key_A_5 },
  { '6',          Key_6,      Key_S_6,    Key_C_6,    Key_A_6 },
  { '7',          Key_7,      Key_S_7,    -1,         Key_A_7 },
  { '8',          Key_8,      Key_S_8,    -1,         Key_A_8 },
  { '9',          Key_9,      Key_S_9,    -1,         Key_A_9 },
  { 'A',          Key_A,      Key_S_A,    Key_C_A,    Key_A_A },
  { 'B',          Key_B,      Key_S_B,    Key_C_B,    Key_A_B },
  { 'C',          Key_C,      Key_S_C,    Key_C_C,    Key_A_C },
  { 'D',          Key_D,      Key_S_D,    Key_C_D,    Key_A_D },
  { 'E',          Key_E,      Key_S_E,    Key_C_E,    Key_A_E },
  { 'F',          Key_F,      Key_S_F,    Key_C_F,    Key_A_F },
  { 'G',          Key_G,      Key_S_G,    Key_C_G,    Key_A_G },
  { 'H',          Key_H,      Key_S_H,    Key_C_H,    Key_A_H },
  { 'I',          Key_I,      Key_S_I,    Key_C_I,    Key_A_I },
  { 'J',          Key_J,      Key_S_J,    Key_C_J,    Key_A_J },
  { 'K',          Key_K,      Key_S_K,    Key_C_K,    Key_A_K },
  { 'L',          Key_L,      Key_S_L,    Key_C_L,    Key_A_L },
  { 'M',          Key_M,      Key_S_M,    Key_C_M,    Key_A_M },
  { 'N',          Key_N,      Key_S_N,    Key_C_N,    Key_A_N },
  { 'O',          Key_O,      Key_S_O,    Key_C_O,    Key_A_O },
  { 'P',          Key_P,      Key_S_P,    Key_C_P,    Key_A_P },
  { 'Q',          Key_Q,      Key_S_Q,    Key_C_Q,    Key_A_Q },
  { 'R',          Key_R,      Key_S_R,    Key_C_R,    Key_A_R },
  { 'S',          Key_S,      Key_S_S,    Key_C_S,    Key_A_S },
  { 'T',          Key_T,      Key_S_T,    Key_C_T,    Key_A_T },
  { 'U',          Key_U,      Key_S_U,    Key_C_U,    Key_A_U },
  { 'V',          Key_V,      Key_S_V,    Key_C_V,    Key_A_V },
  { 'W',          Key_W,      Key_S_W,    Key_C_W,    Key_A_W },
  { 'X',          Key_X,      Key_S_X,    Key_C_X,    Key_A_X },
  { 'Y',          Key_Y,      Key_S_Y,    Key_C_Y,    Key_A_Y },
  { 'Z',          Key_Z,      Key_S_Z,    Key_C_Z,    Key_A_Z },

  { VK_PRIOR,     Key_PgUp,   Key_S_PgUp, Key_C_PgUp, Key_A_PgUp },
  { VK_NEXT,      Key_PgDn,   Key_S_PgDn, Key_C_PgDn, Key_A_PgDn },
  { VK_END,       Key_End,    Key_S_End,  Key_C_End,  Key_A_End },
  { VK_HOME,      Key_Home,   Key_S_Home, Key_C_Home, Key_A_Home },
  { VK_LEFT,      Key_Lft,    Key_S_Lft,  Key_C_Lft,  Key_A_Lft },
  { VK_UP,        Key_Up,     Key_S_Up,   Key_C_Up,   Key_A_Up },
  { VK_RIGHT,     Key_Rgt,    Key_S_Rgt,  Key_C_Rgt,  Key_A_Rgt },
  { VK_DOWN,      Key_Dwn,    Key_S_Dwn,  Key_C_Dwn,  Key_A_Dwn },
  { VK_INSERT,    Key_Ins,    Key_S_Ins,  Key_C_Ins,  Key_A_Ins },
  { VK_DELETE,    Key_Del,    Key_S_Del,  Key_C_Del,  Key_A_Del },
  { VK_CLEAR,     Key_5Num,   Key_S_5Num, Key_C_5Num, Key_A_5Num },
  { VK_NUMPAD0,   Key_0,      Key_S_Ins,  Key_C_Ins,  -1 },
  { VK_NUMPAD1,   Key_1,      Key_S_End,  Key_C_End,  -1 },
  { VK_NUMPAD2,   Key_2,      Key_S_Dwn,  Key_C_Dwn,  -1 },
  { VK_NUMPAD3,   Key_3,      Key_S_PgDn, Key_C_PgDn, -1 },
  { VK_NUMPAD4,   Key_4,      Key_S_Lft,  Key_C_Lft,  -1 },
  { VK_NUMPAD5,   Key_5,      Key_S_5Num, Key_C_5Num, -1 },
  { VK_NUMPAD6,   Key_6,      Key_S_Rgt,  Key_C_Rgt,  -1 },
  { VK_NUMPAD7,   Key_7,      Key_S_Home, Key_C_Home, -1 },
  { VK_NUMPAD8,   Key_8,      Key_S_Up,   Key_C_Up,   -1 },
  { VK_NUMPAD9,   Key_9,      Key_S_PgUp, Key_C_PgUp, -1 },
  { VK_MULTIPLY,  Key_Multi,  Key_Multi,  Key_Multi,  Key_Multi },
  { VK_ADD,       Key_Plus,   Key_Plus,   Key_Plus,   Key_Plus },
  { VK_SUBTRACT,  Key_Minus,  Key_Minus,  Key_Minus,  Key_Minus },
  { VK_DECIMAL,   -1,         -1,         Key_C_Del,  Key_A_Del },
  { VK_DIVIDE,    Key_Sls,    Key_Sls,    Key_Sls,    Key_Sls },
  { VK_F1,        Key_F1,     Key_S_F1,   Key_C_F1,   Key_A_F1 },
  { VK_F2,        Key_F2,     Key_S_F2,   Key_C_F2,   Key_A_F2 },
  { VK_F3,        Key_F3,     Key_S_F3,   Key_C_F3,   Key_A_F3 },
  { VK_F4,        Key_F4,     Key_S_F4,   Key_C_F4,   Key_A_F4 },
  { VK_F5,        Key_F5,     Key_S_F5,   Key_C_F5,   Key_A_F5 },
  { VK_F6,        Key_F6,     Key_S_F6,   Key_C_F6,   Key_A_F6 },
  { VK_F7,        Key_F7,     Key_S_F7,   Key_C_F7,   Key_A_F7 },
  { VK_F8,        Key_F8,     Key_S_F8,   Key_C_F8,   Key_A_F8 },
  { VK_F9,        Key_F9,     Key_S_F9,   Key_C_F9,   Key_A_F9 },
  { VK_F10,       Key_F10,    Key_S_F10,  Key_C_F10,  Key_A_F10 },
  { VK_F11,       Key_F11,    Key_S_F11,  Key_C_F11,  Key_A_F11 },
  { VK_F12,       Key_F12,    Key_S_F12,  Key_C_F12,  Key_A_F12 },

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


//  ------------------------------------------------------------------

bool is_oem_key(int keycode)
{
  switch(keycode)
  {
    // OEM specific keys
    case 0x2a:
    case 0xba: case 0xbb: case 0xbc: case 0xbd: case 0xbe:
    case 0xbf: case 0xc0:
    case 0xdb: case 0xdc: case 0xdd: case 0xde: case 0xdf:
    case 0xe0: case 0xe1: case 0xe2: case 0xe3: case 0xe4:
    case 0xe6:
    case 0xe9: case 0xea: case 0xeb: case 0xec: case 0xed:
    case 0xef: case 0xf0: case 0xf1: case 0xf2: case 0xf3:
    case 0xf4: case 0xf5:
      return true;

    default:
      return false;
  }
}


//  ------------------------------------------------------------------

int gkbd_nt2bios(INPUT_RECORD& inp) {

  int keycode = inp.Event.KeyEvent.wVirtualKeyCode;
  int state   = inp.Event.KeyEvent.dwControlKeyState;
  int ascii   = inp.Event.KeyEvent.uChar.AsciiChar;

  // Look up the virtual keycode in the table. Ignore unrecognized keys.

  kbd* k = &__gkbd_nt2b_table[0];
  while((keycode != k->keycode) and (k->keycode != -1))
    k++;
  if(k->keycode == -1) {  // value not in table
    return ascii ? ascii : -1;
  }

  // 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.

  int c;
  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)
  {
    if (k->shift == -1)
      c = ascii ? ascii : -1;
    else
      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 (g_isupper(keycode) or (k->normal == -1))
      c = ascii ? ascii : -1;
    else
      c = k->normal;
  }

  if (c != -1)
  {
    if (ascii and not (right_alt_same_as_left ? (state & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) : (state & LEFT_ALT_PRESSED)))
      if (isalnum(keycode))
        return (ascii == ' ') ? Key_Space : ascii;
    if (ISEXT(c))
      return EXTVAL(c) << 8;
  }

  return c;
}

//  ------------------------------------------------------------------

bool is_numpad_key(const INPUT_RECORD& inp) {

  if(not (inp.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY)) {
    switch(inp.Event.KeyEvent.wVirtualKeyCode) {
      case VK_CLEAR:
      case VK_PRIOR:
      case VK_NEXT:
      case VK_END:
      case VK_HOME:
      case VK_LEFT:
      case VK_UP:
      case VK_RIGHT:
      case VK_DOWN:
      case VK_INSERT:
      case VK_DELETE:
      case VK_NUMPAD0:
      case VK_NUMPAD1:
      case VK_NUMPAD2:
      case VK_NUMPAD3:
      case VK_NUMPAD4:
      case VK_NUMPAD5:
      case VK_NUMPAD6:
      case VK_NUMPAD7:
      case VK_NUMPAD8:
      case VK_NUMPAD9:
        return true;
    }
  }
  return false;
}


//  ------------------------------------------------------------------
//  Numpad translation table

#elif defined(__MSDOS__) || defined(__OS2__)

const word numpad_keys[] = {
  0x4737, 0x4838, 0x4939, 0x0000,
  0x4B34, 0x0000, 0x4D36, 0x0000,
  0x4F31, 0x5032, 0x5133,
  0x5230, 0x532e
};

#endif

#if defined(__linux__)
bool linux_cui_key(gkey k) {
  switch(k) {
    case Key_Dwn:
    case Key_Up:
    case Key_Lft:
    case Key_Rgt:
    case Key_Home:
    case Key_Del:
    case Key_Ins:
    case Key_PgDn:
    case Key_PgUp:
    case Key_End:
      return true;
  }
  return false;
}
#endif

#if defined(__BEOS__)
int BeOSShiftState()
{
  int shift = 0;
  uint32 mods = modifiers();
  if(mods&B_LEFT_SHIFT_KEY)  shift |= LSHIFT;
  if(mods&B_RIGHT_SHIFT_KEY) shift |= RSHIFT;
  if(mods&B_CONTROL_KEY)     shift |= GCTRL;
  if(mods&B_OPTION_KEY)      shift |= ALT;
  return shift;
}
#endif

//  ------------------------------------------------------------------
//  Get key stroke

gkey kbxget_raw(int mode) {
//  mode - =0 - wait for key is pressed (returns code)
//         =1 - test if keystroke is available (returns code if YES,
//              otherwise returns 0)
//         =2 - return Shifts key status
  gkey k;

//  TO_PORT_TAG: kbxget_raw(3)
  #if defined(__USE_NCURSES__)

  int key;
  if(mode == 2) {
    // We can't do much but we can at least this :-)
    k = kbxget_raw(1);
    key = 0;
    switch(k) {
      case Key_C_Brk:
        key = GCTRL;
        break;
      case Key_S_Tab:
      case Key_S_Home:
      case Key_S_Del:
      case Key_S_Ins:
      case Key_S_Lft:
      case Key_S_Rgt:
      case Key_S_End:
        key = LSHIFT;
        break;
    }
    return key;
  }

  // Get keystroke
  key = gkbd_cursgetch(mode);
  if(key == ERR)
    return 0;

  // Prefix for Meta-key or Alt-key sequences
  if(key == 27) {
    int key2 = gkbd_cursgetch(TRUE);
    // If no key follows, it is no Meta- or Alt- seq, but a single Esc
    if(key2 == ERR)
      k = Key_Esc;
    // Compute the right keycode for the alt sequence
    else if((key2 >= '1') and (key2 <= '9'))
      k = 0x7800 + ((key2 - '1') << 8);
    else if(key2 == '0')
      k = 0x8100;
    else if(g_isalpha(key2))
      k = (scancode_table[key2]);
    else if(key2 == '\010')
      k = Key_A_BS;
    else if(key2 == '\011')
      k = Key_A_Tab;
    else if(key2 == '\015')
      k = Key_A_Ent;
    else {
      // No correct Alt-sequence; ungetch last key and return Esc
      if (mode != 1)
        ungetch(key2);
      k = Key_Esc;
    }

    if((key2 != ERR) and (mode == 1))
      ungetch(key2);
  }
  // Curses sequence; lookup in nice table above
  else if((key >= KEY_MIN) and (key <= KEY_MIN+sizeof(gkbd_curstable)/sizeof(int)))
    k = (gkbd_curstable[key - KEY_MIN]);
  else if(key == '\015')
    k = Key_Ent;
  else if(key == '\011')
    k = Key_Tab;
  else if(key == '\000')
    k = Key_Space;
  else
    k = key;

  if(mode == 1)
    ungetch(key);

  #elif defined(__MSDOS__)

  if(gkbd.extkbd)
    mode |= 0x10;

  i86 cpu;
  cpu.ah((byte)mode);
  cpu.genint(0x16);
  if(mode & 0x01)
    if(cpu.flags() & 0x40)   // if ZF is set, no key is available
      return 0;
  k = (gkey)cpu.ax();

  if((mode & ~0x10) == 0) {
    if((KCodAsc(k) == 0xE0) and (KCodScn(k) != 0)) {
      if(kbxget_raw(2) & (LSHIFT | RSHIFT)) {
        KCodAsc(k) = 0;
        KCodScn(k) |= 0x80;
      }
    }
    else
      switch(KCodScn(k)) {
        case 0x47:
        case 0x48:
        case 0x49:
        case 0x4B:
        case 0x4D:
        case 0x4F:
        case 0x50:
        case 0x51:
        case 0x52:
        case 0x53:
          {
            int shifts = kbxget_raw(2);
            if(shifts & (LSHIFT | RSHIFT)) {
              if(shifts & NUMLOCK)
                KCodAsc(k) = 0;
              else {
                KCodAsc(k) = 0;
                KCodScn(k) |= 0x80;
              }
            }
          }
          break;
        default:
          break;
      }
  }

  // If you test shift/alt/ctrl status with bios calls (e.g., using
  // bioskey (2) or bioskey (0x12)) then you should also use bios calls
  // for testing for keys.  This can be done with by bioskey (1) or
  // bioskey (0x11).  Failing to do so can cause trouble in multitasking
  // environments like DESQview/X. (Taken from DJGPP documentation)
  if((mode & 0x02) == 1)
    kbxget_raw(1);

  #elif defined(__OS2__)

  KBDKEYINFO kb;
  mode &= 0xF;
  if(mode == 0)
    KbdCharIn(&kb, IO_WAIT, 0);
  else if(mode == 2) {
    KbdPeek(&kb, 0);
    if(kb.fbStatus)
      return (gkey)(kb.fsState & (RSHIFT|LSHIFT|GCTRL|ALT));
    else
      return 0;
  }
  else {
    KbdPeek(&kb, 0);
    if(!(kb.fbStatus & 0x40))
      return 0;
  }
  KCodScn(k) = kb.chScan;
  KCodAsc(k) = kb.chChar;
  if(0x000 == KCodKey(k))
    return KEY_BRK;
  if(0xE0 == KCodScn(k))
    KCodScn(k) = 0x1C;
  else {
    if(0xE0 == KCodAsc(k)) {
      // If key on the alphanumeric part then don't touch it.
      // This need to enter for example, russian 'p' char (code 0xe0)
      if(KCodScn(k) >= 0x38) {
        KCodAsc(k) = 0x00;
        if(kb.fsState & (LSHIFT | RSHIFT))
          KCodScn(k) |= 0x80;
      }
      else
        KCodScn(k) = 0x00;
    }
    else
      switch(KCodScn(k)) {
        case 0x47:
        case 0x48:
        case 0x49:
        case 0x4B:
        case 0x4D:
        case 0x4F:
        case 0x50:
        case 0x51:
        case 0x52:
        case 0x53:
          if(kb.fsState & (LSHIFT | RSHIFT)) {
            if(kb.fsState & NUMLOCK)
              KCodAsc(k) = 0;
            else {
              KCodAsc(k) = 0;
              KCodScn(k) |= 0x80;
            }
          }
          break;
        default:
          break;
      }
  }

  #elif defined(__WIN32__)

  INPUT_RECORD inp;
  DWORD nread;
  static gkey KeyCtrlState = 0;

  if (mode == 3) {
    return KeyCtrlState;
  }
  else if(mode == 2) {
    return 0;
  }
  else if(mode & 0x01) {

    // Peek at next key
    k = 0;
    PeekConsoleInput(gkbd_hin, &inp, 1, &nread);
    if(nread) {
      if((inp.EventType == KEY_EVENT) and inp.Event.KeyEvent.bKeyDown) {
        int kc = gkbd_nt2bios(inp);
        if((kc != -1) or is_oem_key(inp.Event.KeyEvent.wVirtualKeyCode)) {
          k = (gkey)kc;
          return k;
        }
      }

      if ((inp.EventType != MOUSE_EVENT) || (WinVer.dwPlatformId == VER_PLATFORM_WIN32_NT))
      {
        // Discard other events
        ReadConsoleInput(gkbd_hin, &inp, 1, &nread);
      }
    }
  }
  else {

    DWORD &CKS = inp.Event.KeyEvent.dwControlKeyState;
    WORD &VKC = inp.Event.KeyEvent.wVirtualKeyCode;
    char &ascii = inp.Event.KeyEvent.uChar.AsciiChar;

    while(1) {

      PeekConsoleInput(gkbd_hin, &inp, 1, &nread);
      if(not nread) {
        WaitForSingleObject(gkbd_hin, 1000);
        continue;
      }

      if((inp.EventType == KEY_EVENT) and inp.Event.KeyEvent.bKeyDown) {
        bool alt_pressed = make_bool(CKS & (LEFT_ALT_PRESSED|RIGHT_ALT_PRESSED));
        bool ctrl_pressed = make_bool(CKS & (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED));
        bool shift_pressed = make_bool(CKS & SHIFT_PRESSED);
        bool special_key = false;

        k = 0;

        if(alt_pressed)
          special_key = is_numpad_key(inp); // Alt-<numpad key>
        else if(not gkbd_nt and not (CKS & ENHANCED_KEY) and not (VKC == VK_CLEAR) and (ascii and not ctrl_pressed) and not (iscntrl(ascii) and shift_pressed))
          special_key = true; // It is alphanumeric key under Win9x
        if(special_key) {
          ReadConsole(gkbd_hin, &ascii, 1, &nread, NULL);
          if(alt_pressed) {
            k = (gkey)ascii;
            break;
          }
        }
        else {
          ReadConsoleInput(gkbd_hin, &inp, 1, &nread);
        }

        // Fix Win9x anomaly
        if((CKS & NUMLOCK_ON) and not (CKS & ENHANCED_KEY) and (VKC == VK_DELETE))
          VKC = VK_DECIMAL;

        switch(VKC) {
          // Not meanful keys
          case VK_SHIFT:
          case VK_CONTROL:
          case VK_MENU:
          case VK_CAPITAL:
          case VK_NUMLOCK:
          case VK_SCROLL:
            break;

          case VK_NUMPAD0:
          case VK_NUMPAD1:
          case VK_NUMPAD2:
          case VK_NUMPAD3:
          case VK_NUMPAD4:
          case VK_NUMPAD5:
          case VK_NUMPAD6:
          case VK_NUMPAD7:
          case VK_NUMPAD8:
          case VK_NUMPAD9:
            if(shift_pressed) {
              WORD keytrans[10][2] = {
                {VK_NUMPAD0, VK_INSERT},
                {VK_NUMPAD1, VK_END},
                {VK_NUMPAD2, VK_DOWN},
                {VK_NUMPAD3, VK_NEXT},
                {VK_NUMPAD4, VK_LEFT},
                {VK_NUMPAD5, VK_CLEAR},
                {VK_NUMPAD6, VK_RIGHT},
                {VK_NUMPAD7, VK_HOME},
                {VK_NUMPAD8, VK_UP},
                {VK_NUMPAD9, VK_PRIOR},
              };
              for(int i = 0; i < 10; i++)
                if(VKC == keytrans[i][0]) {
                  VKC = keytrans[i][1];
                  break;
                }
            }
            // fall through
          default:
            {
              int kc = gkbd_nt2bios(inp);
              if(kc != -1)
                k = (gkey)kc;
            }
            break;
        }
        if(k != 0)
          break;
      }
      else {
        // Discard other events
        ReadConsoleInput(gkbd_hin, &inp, 1, &nread);
      }
    }
  }

  #elif defined(__UNIX__)

  if(mode == 2) {
    int key;
    #if defined(__linux__)
    // Under Linux we could use TIOCLINUX fn. 6 to read shift states on console
    // Of course it is very unportable but should produce good results :-)
    key = 6;
    if(ioctl(fileno(stdin), TIOCLINUX, &key) == -1)
    #endif
      key = 0;
    #ifdef __BEOS__
    key = BeOSShiftState();
    #endif
    return key;
  }
  else if(mode & 0x01) {

    // Peek at next key
    return gkbd_input_pending() ? 0xFFFF : 0;
  }
  else {

    k = gkbd_getmappedkey();
  }

  #endif

  #ifdef __linux__
  if(linux_cui_key(k)) {
    // Under Linux we could use TIOCLINUX fn. 6 to read shift states on console
    // Of course it is very unportable but should produce good results :-)
    int shifts = 6;
    if(ioctl(fileno(stdin), TIOCLINUX, &shifts) == -1)
      shifts = 0;
    if(shifts & (LSHIFT | RSHIFT))
      KCodScn(k) |= 0x80;
    else if(shifts & GCTRL) {
      switch(k) {
        case Key_Ins:
          k = Key_C_Ins;
          break;
        case Key_Del:
          k = Key_C_Del;
          break;
        case Key_Dwn:
          k = Key_C_Dwn;
          break;
        case Key_Up:
          k = Key_C_Up;
          break;
        case Key_Lft:
          k = Key_C_Lft;
          break;
        case Key_Rgt:
          k = Key_C_Rgt;
          break;
        case Key_Home:
          k = Key_C_Home;
          break;
        case Key_PgDn:
          k = Key_C_PgDn;
          break;
        case Key_PgUp:
          k = Key_C_PgUp;
          break;
        case Key_End:
          k = Key_C_End;
          break;
      }
    }
  } else if(k == Key_BS) {
    // Under Linux we could use TIOCLINUX fn. 6 to read shift states on console
    // Of course it is very unportable but should produce good results :-)
    int shifts = 6;
    if(ioctl(fileno(stdin), TIOCLINUX, &shifts) == -1)
      shifts = 0;
    if(shifts & ALT)
      k = Key_A_BS;
    else if(shifts & GCTRL)
      k = Key_C_BS;
  }
  #elif __BEOS__
  {
    int shifts = BeOSShiftState();
    if(shifts & (ALT))
      switch(k){
        case Key_0: k=Key_A_0; break;
        case Key_1: k=Key_A_1; break;
        case Key_2: k=Key_A_2; break;
        case Key_3: k=Key_A_3; break;
        case Key_4: k=Key_A_4; break;
        case Key_5: k=Key_A_5; break;
        case Key_6: k=Key_A_6; break;
        case Key_7: k=Key_A_7; break;
        case Key_8: k=Key_A_8; break;
        case Key_9: k=Key_A_9; break;
        case Key_A: k=Key_A_A; break;
        case Key_B: k=Key_A_B; break;
        case Key_C: k=Key_A_C; break;
        case Key_D: k=Key_A_D; break;
        case Key_E: k=Key_A_E; break;
        case Key_F: k=Key_A_F; break;
        case Key_G: k=Key_A_G; break;
        case Key_H: k=Key_A_H; break;
        case Key_I: k=Key_A_I; break;
        case Key_J: k=Key_A_J; break;
        case Key_K: k=Key_A_K; break;
        case Key_L: k=Key_A_L; break;
        case Key_M: k=Key_A_M; break;
        case Key_N: k=Key_A_N; break;
        case Key_O: k=Key_A_O; break;
        case Key_P: k=Key_A_P; break;
        case Key_Q: k=Key_A_Q; break;
        case Key_R: k=Key_A_R; break;
        case Key_S: k=Key_A_S; break;
        case Key_T: k=Key_A_T; break;
        case Key_U: k=Key_A_U; break;
        case Key_V: k=Key_A_V; break;
        case Key_W: k=Key_A_W; break;
        case Key_X: k=Key_A_X; break;
        case Key_Y: k=Key_A_Y; break;
        case Key_Z: k=Key_A_Z; break;
        case Key_F1: k=Key_A_F1; break;
        case Key_F2: k=Key_A_F2; break;
        case Key_F3: k=Key_A_F3; break;
        case Key_F4: k=Key_A_F4; break;
        case Key_F5: k=Key_A_F5; break;
        case Key_F6: k=Key_A_F6; break;
        case Key_F7: k=Key_A_F7; break;
        case Key_F8: k=Key_A_F8; break;
        case Key_F9: k=Key_A_F9; break;
        case Key_F10: k=Key_A_F10; break;
        case Key_F11: k=Key_A_F11; break;
        case Key_F12: k=Key_A_F12; break;
        case Key_BS:  k=Key_A_BS; break;
        case Key_Ent: k=Key_A_Ent; break;
        case Key_Tab: k=Key_A_Tab; break;
        case Key_Dwn: k = Key_A_Dwn; break;
        case Key_Up:  k = Key_A_Up;  break;
        case Key_Lft: k = Key_A_Lft; break;
        case Key_Rgt: k = Key_A_Rgt; break;
        case Key_Home: k = Key_A_Home; break;
        case Key_PgDn: k = Key_A_PgDn; break;
        case Key_PgUp: k = Key_A_PgUp; break;
        case Key_End: k = Key_A_End; break;
        // case Key_: k=Key_A_; break;
        default: break;
      }
    else if(shifts & (LSHIFT | RSHIFT))
      switch(k){
        case Key_F1: k=Key_S_F1; break;
        case Key_F2: k=Key_S_F2; break;
        case Key_F3: k=Key_S_F3; break;
        case Key_F4: k=Key_S_F4; break;
        case Key_F5: k=Key_S_F5; break;
        case Key_F6: k=Key_S_F6; break;
        case Key_F7: k=Key_S_F7; break;
        case Key_F8: k=Key_S_F8; break;
        case Key_F9: k=Key_S_F9; break;
        case Key_F10: k=Key_S_F10; break;
        case Key_F11: k=Key_S_F11; break;
        case Key_F12: k=Key_S_F12; break;
        case Key_Tab: k=Key_S_Tab; break;
        default: KCodScn(k) |= 0x80; break;
      }
    else if(shifts & GCTRL) {
      switch(k) {
        case Key_Ent: k = Key_C_Ent; break;
        case Key_Ins: k = Key_C_Ins; break;
        case Key_Del: k = Key_C_Del; break;
        case Key_Dwn: k = Key_C_Dwn; break;
        case Key_Up:  k = Key_C_Up;  break;
        case Key_Lft: k = Key_C_Lft; break;
        case Key_Rgt: k = Key_C_Rgt; break;
        case Key_Home: k = Key_C_Home; break;
        case Key_PgDn: k = Key_C_PgDn; break;
        case Key_PgUp: k = Key_C_PgUp; break;
        case Key_End: k = Key_C_End; break;
        case Key_BS: k = Key_C_BS; break;
        case Key_F1: k=Key_C_F1; break;
        case Key_F2: k=Key_C_F2; break;
        case Key_F3: k=Key_C_F3; break;
        case Key_F4: k=Key_C_F4; break;
        case Key_F5: k=Key_C_F5; break;
        case Key_F6: k=Key_C_F6; break;
        case Key_F7: k=Key_C_F7; break;
        case Key_F8: k=Key_C_F8; break;
        case Key_F9: k=Key_C_F9; break;
        case Key_F10: k=Key_C_F10; break;
        case Key_F11: k=Key_C_F11; break;
        case Key_F12: k=Key_C_F12; break;
      }
    }
  }
  #endif

//  TO_PORT_TAG: kbxget_raw(3)
#if defined(__WIN32__)
  KeyCtrlState = (gkey)(inp.Event.KeyEvent.dwControlKeyState & 0xFFFF);
#endif
  return k;
}


//  ------------------------------------------------------------------
//  Get key stroke

gkey kbxget(int mode) {

  return keyscanxlat(kbxget_raw(mode));
}


//  ------------------------------------------------------------------
//  Returns keycode of waiting key or zero if none

gkey kbxhit() {

  return kbxget(0x01);
}


//  ------------------------------------------------------------------
//  Clears internal keyboard buffer

void kbclear() {

  while(gkbd.kbuf != NULL) {

    KBuf *kbuf = gkbd.kbuf->next;
    throw_free(gkbd.kbuf);
    gkbd.kbuf = kbuf;
  }
}


//  ------------------------------------------------------------------
//  Clear keyboard buffer

void clearkeys() {

  while(kbxhit())
    kbxget(0x00);
}


//  ------------------------------------------------------------------
//  Puts a keystroke into the CXL keyboard "buffer"

bool gKeystacking = false;

int kbput(gkey xch)
{
  if (gKeystacking)
  {
    if (gkbd.kbuf != NULL)
      return -1;

    gKeystacking = false;
  }

  KBuf* kbuf;
  KBuf* temp;

  // allocate space for another keypress record
  kbuf=(KBuf*)throw_malloc(sizeof(KBuf));

  // find last record in linked list
  if((temp=gkbd.kbuf)!=NULL)
    for(;temp->next!=NULL;temp=temp->next);

  // add new record to end of linked list
  kbuf->next=NULL;
  kbuf->prev=temp;
  if(temp != NULL)
    temp->next=kbuf;

  // add keypress info to new record
  kbuf->xch=xch;

  // if kbuf pointer was NULL, point it to new record
  if(gkbd.kbuf == NULL)
    gkbd.kbuf=kbuf;

  // return normally
  return 0;
}


//  ------------------------------------------------------------------
//  Put keys into the real keyboard buffer

gkey kbput_(gkey xch) {

  #if defined(__MSDOS__)

  #if defined(__DJGPP__)
  if(gkbd.extkbd) {
    i86 cpu;

    cpu.ah(0x05);
    cpu.cx((word)xch);
    cpu.genint(0x16);
  }
  else {
  #endif

  #define BufStart (word)peek(0x40,0x80)
  #define BufEnd   (word)peek(0x40,0x82)
  #define BufHead  (word)peek(0x40,0x1A)
  #define BufTail  (word)peek(0x40,0x1C)
  #define BufTail_(a) poke(0x40,0x1C,(word)(a))

  word OldBufTail;

  OldBufTail = BufTail;
  if(BufTail == BufEnd-2)
    BufTail_(BufStart);
  else
    BufTail_(BufTail+2);

  if(BufTail == BufHead)
    BufTail_(OldBufTail);
  else {
    poke(0x40, OldBufTail, xch);
  }

  #if defined(__DJGPP__)
  }
  #endif

  #endif

  return xch;
}


//  ------------------------------------------------------------------
//  Put keys into the real keyboard buffer

void kbputs_(char* str) {

  char* p;

  for(p=str; *p ;p++)
    kbput_(gkey((scancode(*p)<<8)|*p));
}


//  ------------------------------------------------------------------
//  Change defined "on-key" list pointer

KBnd* chgonkey(KBnd* list) {

  KBnd* temp;

  temp = gkbd.onkey;
  gkbd.onkey = list;

  return temp;
}


//  ------------------------------------------------------------------
//  Frees all active onkey definitions from memory

void freonkey() {

  KBnd* temp;

  // free all onkey records in linked list
  while(gkbd.onkey!=NULL) {
    temp = gkbd.onkey->prev;
    throw_free(gkbd.onkey);
    gkbd.onkey = temp;
  }
}


//  ------------------------------------------------------------------
//  Attaches/detaches a key to a function

int setonkey(gkey keycode, VfvCP func, gkey pass) {

  // search for a keycode that is already defined
  KBnd* onkey = gkbd.onkey;
  while(onkey) {
    if(onkey->keycode == keycode)
      break;
    onkey = onkey->prev;
  }

  // check to see if a key detachment is being requested
  if(func == NULL) {

    // if no defined onkey was found, then error
    if(onkey == NULL)
      return 2;

    // delete record from linked list
    KBnd* prev = onkey->prev;
    KBnd* next = onkey->next;
    if(prev)
      prev->next = next;
    if(next)
      next->prev = prev;
    if(onkey == gkbd.onkey)
      gkbd.onkey = prev;

    // free memory allocated for deleted record
    throw_free(onkey);
  }
  else {

    // if key was found, change func pointer
    // otherwise create a new onkey record
    if(onkey)
      onkey->func = func;
    else {

      // allocate memory for new record
      onkey = (KBnd*)throw_malloc(sizeof(KBnd));
      if(onkey == NULL)
        return 1;

      // add new record to linked list
      if(gkbd.onkey)
        gkbd.onkey->next = onkey;
      onkey->prev = gkbd.onkey;
      onkey->next = NULL;
      gkbd.onkey = onkey;

      // save info in onkey record
      gkbd.onkey->keycode = keycode;
      gkbd.onkey->func = func;
      gkbd.onkey->pass = pass;
    }
  }

  // return normally
  return 0;
}


//  ------------------------------------------------------------------

gkey key_tolower(gkey __keycode) {

  byte &ascii = KCodAsc(__keycode);
  if(g_isupper(ascii))
    ascii = g_tolower(ascii);
  return __keycode;
}


//  ------------------------------------------------------------------
