// doesn't handle all keys correctly, I'm to lazy to be bothered
// keypad /
// and keyboard -
// on German keyboard for example are not distinguished...

/*****************************************************************************/
/*                                                                           */
/*   LIBRARY Keyboard functions                    Copyright K.Wilkins 1996  */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*   Title      : Keyboard handler functions                                 */
/*                                                                           */
/*   File Name  : KEYBOARD.C                                                 */
/*                                                                           */
/*   Author     : Keith Wilkins                                              */
/*                                                                           */
/*   Version    : 1.00                                                       */
/*                                                                           */
/*   Desciption : This file contains the functions handle the keyabord       */
/*                input and control.                                         */
/*                                                                           */
/*                These functions are the most PRIMATIVE Level of IO and     */
/*                as such are the fastest.                                   */
/*                                                                           */
/*   Functions  : fKBDinit()      - Initialise the I/O System                */
/*                fKBDrestore()   - Restore I/O to its virgin state          */
/*                fKBDgetch()     - Wait and get a pressed key               */
/*                fKBDungetch()   - Wait and get a pressed key               */
/*                fKBDhit()       - Is a key being pressed                   */
/*                fKBDisdown()    - Check a particular key for press         */
/*                fKBDfuncreg()   - Map a function pointer to a keypress     */
/*                fKBDdokey()     - Execute a function if a key is pressed   */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*   Revision History:                                                       */
/*                                                                           */
/*   Version    Date    Who  Description of changes                          */
/*   -------    ----    ---  ----------------------                          */
/*                                                                           */
/*    0.01    20/03/93  K.W  Creation                                        */
/*                                                                           */
/*    0.02    04/06/94  K.W  Mouse routines added, initial code lifted from  */
/*                           DEU 5.2 Courstesy of Raphael Quinet             */
/*                                                                           */
/*    0.03    01/11/94  K.W  Mods to use new bailout fFatal, non-returning   */
/*                                                                           */
/*    0.04    12/11/94  K.W  Removed keyboard code from GFX.C and interfaced */
/*                           to LMW's keyboard handler.                      */
/*    1.00    05/08/96  K.W  Public release of DVE V1.0                      */
/*                                                                           */
/*****************************************************************************/
/* These functions MUST NOT use any fDebug calls as they may be begin used   */
/* to setup the debug window and as such will generate recusive function     */
/* calls until the stack if they call fDebug whilst it is being setup        */
/*****************************************************************************/

#include <dos.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>

#include "standard.h"
#include "l_level.h"
#include "vesa.h"
#include "error.h"

#include "keyboard.h"

/***************/
/* Scan buffer */
/***************/
volatile T_UCHAR abScanKeyMap[NO_OF_SCANMAPS];

T_INT keymap=GERMAN_KEYMAP;

/*************************************/
/* Scan to Key code conversion array */
/*************************************/
#include "keyconv.h"

/*******************/
/* Keyboard buffer */
/*******************/
#define  KEYBUFSIZE     512
static   T_SWORD        aiKeyBuff[KEYBUFSIZE];
static   volatile T_SWORD iKeyBuffPtr=-1;


/**************************************************************************/
/* The keyboard function pointer array holds a function pointer for every */
/* key on the keyboard, both normal and extended chars are handled        */
/*                                                                        */
/*               000-127 Mapped to ASCII codes 00-7f                      */
/*               128-384 Mapped to Extended Keyboard codes 0000-00ff      */
/**************************************************************************/
#define NO_OF_KEYMAPS   385
static T_VOID(*apfKeyboard_Map[NO_OF_KEYMAPS])(T_UWORD);
T_INT bKeyBufferEnable=T_TRUE;

T_VOID fKBDnotused(T_UWORD tKeyVal)
{
 T_DEBUG_ENTRY("fKBDnotused",80)
 tKeyVal=0;  /* Stop compiler warnings */
 T_DEBUG_LEAVE
//  putch(7);
}

/******************************/
/* DOS Keyboard vector handle */
/******************************/
static T_VOID __interrupt *fOld_key_handler;
static T_VOID __interrupt fKBDhandler(T_VOID);

T_INT KBDKeyPressed;
static T_INT KBDinitialised=T_FALSE;

/*****************************************************************************/
/*                                                                           */
/*   Function    : fKBDinit()                                                */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : This function intercepts the keyabord interrupt and       */
/*                 installs a new one.                                       */
/*                                                                           */
/*   Accepts     : nothing                                                   */
/*                                                                           */
/*   Returns     : nothing                                                   */
/*                                                                           */
/*****************************************************************************/

T_INT fKBDinit(T_VOID)
{
 T_UWORD  iLoop;
/**********************************************************************/
/* Set all entries of the KeyMap to fNotUsed, makes a beep if pressed */
/**********************************************************************/
 T_DEBUG_ENTRY("fKBDinit",7)
 for(iLoop=0;iLoop<NO_OF_KEYMAPS;iLoop++)
 {
  apfKeyboard_Map[iLoop]=fKBDnotused;
 }
/************************************/
// Intercept the keyboard interrupt */
/************************************/
 fOld_key_handler=_dos_getvect(0x09);
 _dos_setvect(0x09,fKBDhandler);

 /**************************/
 /* Flush the scan key map */
 /**************************/
 memset((T_VOID *)abScanKeyMap,0,NO_OF_SCANMAPS);
 KBDinitialised=T_TRUE;
 T_DEBUG_LEAVE
 return(T_TRUE);
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : fKBDrestore()                                             */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : Restore the old keyboard handler.                         */
/*                                                                           */
/*   Accepts     : nothing                                                   */
/*                                                                           */
/*   Returns     : T_INT T_TRUE=OK T_FALSE=VERROR                                  */
/*                                                                           */
/*****************************************************************************/

T_INT fKBDrestore(T_VOID)
{
 T_DEBUG_ENTRY("fKBDrestore",7)
 if (KBDinitialised)
  _dos_setvect(0x09,fOld_key_handler);
 KBDinitialised=T_FALSE;
 T_DEBUG_LEAVE
 return(T_TRUE);
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : fKBDbuffering()                                           */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : Switch keyboard buffering ON/OFF.                         */
/*                                                                           */
/*   Accepts     : nothing                                                   */
/*                                                                           */
/*   Returns     : T_INT T_TRUE=OK T_FALSE=VERROR                                  */
/*                                                                           */
/*****************************************************************************/

T_VOID fKBDbuffering(T_INT bState)
{
 T_DEBUG_ENTRY("fKBDbuffering",7)
 bKeyBufferEnable=bState;
 while(KBDKeyPressed)
  fKBDgetch();
 T_DEBUG_LEAVE
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : fKBDhandler()                                             */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : This function intercepts the keyboard interrupt and       */
/*                 processes the interrupt.                                  */
/*                                                                           */
/*   Accepts     : nothing                                                   */
/*                                                                           */
/*   Returns     : nothing                                                   */
/*                                                                           */
/*****************************************************************************/

static T_VOID __interrupt fKBDhandler(T_VOID)
{
 /*****************************************************/
 /* -=- get the scan code of the key just pressed -=- */
 /*****************************************************/
 T_UCHAR cScan_code = inp(0x60);
 static T_SWORD iKeyCode=0;
 static T_SWORD iOffset=0;
 static T_SWORD last_was_e0=T_FALSE;

 /************************************************/
 /* -=- if it is an 0xe0 or 0 then ignore it -=- */
 /************************************************/

 if ((cScan_code != 0xe0) && (cScan_code != 0))
 {
  /********************************************************/
  /* Step one setup the SCAN map table, ONLY BASE VALUES  */
  /* -=- if the top bit is set then its a key release -=- */
  /********************************************************/
  if (cScan_code&0x80)
  {
   /************************************/
   /* -=- mark the key as released -=- */
   /************************************/
   // strange this... it is a must for shifted cursor keys... ???
   if ((SCAN_LSHIFT==(cScan_code&0x7f))||(SCAN_LSHIFT==(cScan_code&0x7f)))
   {
    if (last_was_e0==T_FALSE)
    {
     abScanKeyMap[cScan_code&0x7f] = 0;
    }
   }
   else
    abScanKeyMap[cScan_code&0x7f] = 0;
  }
  else
  {
   /***********************************/
   /* -=- make the key as pressed -=- */
   /***********************************/
   abScanKeyMap[cScan_code&0x7f] = 0x01;
   abScanKeyMap[0] = cScan_code;
  }
  /**********************************************************/
  /* Step two process the input scancode based on meta-keys */
  /* only register keys on a press DOWN stroke              */
  /**********************************************************/
  if (!(cScan_code&0x80) && (bKeyBufferEnable || (!bKeyBufferEnable&&!KBDKeyPressed)))
  {
   switch(cScan_code)
   {
    case SCAN_LSHIFT: /**********************************/
    case SCAN_RSHIFT: /* Don't buffer any of these keys */
    case SCAN_CTRL:   /**********************************/
    case SCAN_ALT:
     break;
    default:
     /*********************************************************/
     /* First calcuate the offset caused by any active meta's */
     /*********************************************************/

     if ((abScanKeyMap[SCAN_LSHIFT]==0x01) || (abScanKeyMap[SCAN_RSHIFT]==0x01))
     {
      iOffset=SCAN_SHIFT_OFFSET;
     }
     else if(abScanKeyMap[SCAN_CTRL])
     {
      iOffset=SCAN_CTRL_OFFSET;
     }
     else if(abScanKeyMap[SCAN_ALT])
     {
      iOffset=SCAN_ALT_OFFSET;
     }
     else
     {
      iOffset=SCAN_BASE_OFFSET;
     }
     /***************************************************************/
     /* If this is an extended char then stuff a 0x00 in the buffer */
     /***************************************************************/
     if (iKeyBuffPtr<=KEYBUFSIZE && asScanToKeyConvert[(int)cScan_code+iOffset].iExtended)
     {
      aiKeyBuff[++iKeyBuffPtr]=0x00;
     }

     /*********************************/
     /* Now convert this to a keycode */
     /*********************************/
     iKeyCode=asScanToKeyConvert[(int)cScan_code+iOffset].iKeyCode;
     /***********************************/
     /* Place the key in the buffer     */
     /* Do the equivalent of an ungetch */
     /***********************************/
     if (iKeyBuffPtr<=KEYBUFSIZE)
     {
      aiKeyBuff[++iKeyBuffPtr]=iKeyCode;
      KBDKeyPressed=T_TRUE;
     }
     if ((int)cScan_code+iOffset==SCAN_CTRL_C)
     {
      outp(0x20,0x20);
      fKBDrestore();
      abort();
     }
     break;
   }
  }
  last_was_e0=T_FALSE;
 }
 else
  last_was_e0=T_TRUE;
 /*****************************/
 /* -=- ack the interrupt -=- */
 /*****************************/
 outp(0x20,0x20);
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : fKBDgetch()                                               */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : Wait for a keypress and return the value                  */
/*                                                                           */
/*   Accepts     : nothing                                                   */
/*                                                                           */
/*   Returns     : WORD key pressed                                          */
/*                                                                           */
/*****************************************************************************/

T_UWORD fKBDgetch(T_VOID)
{
 T_UWORD iKey;
 T_DEBUG_ENTRY("fKBDgetch",7)
 /***********************************************/
 /* Wait until there is something in the buffer */
 /***********************************************/
 while(iKeyBuffPtr==-1)
  ;
 /*********************************/
 /* Fetch the key from the buffer */
 /*********************************/
 _disable();
 iKey=aiKeyBuff[iKeyBuffPtr--];
 if(iKeyBuffPtr==-1)
  KBDKeyPressed=T_FALSE;
 _enable();


 T_DEBUG_LEAVE
 return(iKey);
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : fKBDungetch()                                             */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : Return a key press to the input queue                     */
/*                                                                           */
/*   Accepts     : nothing                                                   */
/*                                                                           */
/*   Returns     : WORD key pressed                                          */
/*                                                                           */
/*****************************************************************************/

T_VOID fKBDungetch(T_UWORD tKeypress)
{
 T_DEBUG_ENTRY("fKBDungetch",7)
 _disable();
 if (iKeyBuffPtr<=KEYBUFSIZE)
 {
  aiKeyBuff[++iKeyBuffPtr]=tKeypress;
//    KBDKeyPressed=T_TRUE;
 }
 _enable();
 T_DEBUG_LEAVE
 return;
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : fKBDhit()                                                 */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : Checks if a key is being pressed. and returns the value   */
/*                 of the key being pressed or zero if nothing pressed.      */
/*                                                                           */
/*   Accepts     : nothing                                                   */
/*                                                                           */
/*   Returns     : BYTE flag T_TRUE/T_FALSE if key is being pressed              */
/*                                                                           */
/*****************************************************************************/

T_INT fKBDhit(T_VOID)
{
 return (KBDKeyPressed);
}

T_INT fKBDishit(T_VOID)
{
 T_UINT iKey=0;
 T_DEBUG_ENTRY("fKBDhit",7)
 if (KBDKeyPressed)
  iKey=fKBDgetch();
 T_DEBUG_LEAVE
 return iKey;
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : fKBDisdown()                                              */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : Checks if a particular key is being held down at that     */
/*                 moment                                                    */
/*                                                                           */
/*   Accepts     : scan code to check                                        */
/*                                                                           */
/*   Returns     : BYTE flag T_TRUE/T_FALSE if key is being pressed              */
/*                                                                           */
/*****************************************************************************/

T_INT fKBDisdown(T_UWORD iScancode)
{
 T_DEBUG_ENTRY("fKBDisdown",7)
 if (abScanKeyMap[iScancode])
 {
  T_DEBUG_FUNCTION_POSITION(1)
  T_DEBUG_LEAVE
  return (T_TRUE);
 }
 T_DEBUG_LEAVE
 return (T_FALSE);
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : fKBDfuncreg()                                             */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : Associates a function with a key and that function is     */
/*                 called whenever that key is pressed.                      */
/*                                                                           */
/*   Accepts     : WORD key number, function pointer with WORD argument      */
/*                                                                           */
/*   Returns     : BYTE T_TRUE/T_FALSE is OK                                     */
/*                                                                           */
/*****************************************************************************/

T_INT fKBDfuncreg(T_UWORD tKeyNum,T_VOID(*pfKeyFunc)(T_UWORD))
{
 T_DEBUG_ENTRY("fKBDfuncreg",7)
 if(pfKeyFunc==NULL)
 {
  T_DEBUG_FUNCTION_POSITION(1)
  T_DEBUG_LEAVE
  tool_exit(KEY_NULL_POUNTER);
 }
 if(tKeyNum>=NO_OF_KEYMAPS)
 {
  T_DEBUG_FUNCTION_POSITION(2)
  T_DEBUG_LEAVE
  tool_exit(KEY_BAD_INDEX);
 }
 apfKeyboard_Map[tKeyNum]=pfKeyFunc;
 T_DEBUG_LEAVE
 return(T_TRUE);
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : fKBDdokey()                                               */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : This function checks if a key has been pressed and runs   */
/*                 the associated function of that key.                      */
/*                                                                           */
/*   Accepts     : nothing                                                   */
/*                                                                           */
/*   Returns     : nothing                                                   */
/*                                                                           */
/*****************************************************************************/

T_VOID fKBDdokey(T_VOID)
{
 T_UWORD tUserInput;
 T_UWORD tFunctionIndex;

 T_DEBUG_ENTRY("fKBDdokey",7)
 if(fKBDhit()==T_TRUE)
 {
  /*****************************************************************/
  /* Calculate array index of function pointer from keyboard input */
  /*****************************************************************/
  tUserInput=fKBDgetch();
  if (tUserInput==0)
  {
   tFunctionIndex=128;
   if (!fKBDhit())
   {
    T_DEBUG_FUNCTION_POSITION(1)
    T_DEBUG_LEAVE
    /*******************************************************/
    /* Only half an extended char found in buffer          */
    /* so well quit coz I don't know what else to do ????? */
    /*******************************************************/
    return;
   }
   else
   {
    tUserInput=fKBDgetch();
   }
  }
  else
  {
   tFunctionIndex=0;
  }
  tFunctionIndex+=tUserInput;

  /**************************************************/
  /* Now call the function associated with that key */
  /* If the index is valid                          */
  /**************************************************/
  if (tFunctionIndex && tFunctionIndex<NO_OF_KEYMAPS)
  {
   (apfKeyboard_Map[tFunctionIndex])(tFunctionIndex);
  }
  else
  {
   T_DEBUG_FUNCTION_POSITION(2)
   T_DEBUG_LEAVE
   tool_exit(KEY_BAD_INDEX);
  }
 }
 T_DEBUG_LEAVE
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : fKBDbufferenable()                                        */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : This function enables/disables the keyboard buffering     */
/*                                                                           */
/*   Accepts     : nothing                                                   */
/*                                                                           */
/*   Returns     : nothing                                                   */
/*                                                                           */
/*****************************************************************************/

T_VOID fKBDbufferenable(T_INT bState)
{
 T_DEBUG_ENTRY("fKBDbufferenable",7)
 bKeyBufferEnable=bState;
 while(fKBDhit())
  fKBDgetch();
 T_DEBUG_LEAVE
}

T_INT fKBDisALT(T_VOID)
{
 if (inp(0x60)==SCAN_ALT)
  return T_TRUE;
 return T_FALSE;
}
T_INT fKBDisCTRL(T_VOID)
{
 if (inp(0x60)==SCAN_CTRL)
  return T_TRUE;
 return T_FALSE;
}
T_INT fKBDisRSHIFT(T_VOID)
{
 if (inp(0x60)==SCAN_RSHIFT)
  return T_TRUE;
 return T_FALSE;
}
T_INT fKBDisLSHIFT(T_VOID)
{
 if (inp(0x60)==SCAN_LSHIFT)
  return T_TRUE;
 return T_FALSE;
}
