
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <linux/kd.h>
#include <unistd.h>
#include <string.h>
#include "keymap.h"

static unsigned long oldmodes;
static unsigned char shift_state=0;

/* Shift state bits */
#define RSHIFT_BIT	0x01
#define LSHIFT_BIT	0x02
#define CTRL_BIT	0x04
#define ALT_BIT		0x08
#define SCROLL_BIT	0x10
#define NUMLK_BIT	0x20
#define CAPSLK_BIT	0x40
#define INSERT_BIT	0x80
#define STATE_SHIFT(X)	(X&(RSHIFT_BIT|LSHIFT_BIT))
#define STATE_CAPS(X)	(X&CAPSLK_BIT)
#define STATE_CTRL(X)	(X&CTRL_BIT)
#define STATE_ALT(X)	(X&ALT_BIT)
#define STATE_SHAPS(X)	(STATE_SHIFT(X) || STATE_CAPS(X))

#define UP_BIT		0x80


LIBRARY int Init_Keyboard(void)
{
   if ( ioctl(0, KDGKBMODE, &oldmodes) < 0 )
	return(-1);
   if ( ioctl(0, KDSKBMODE, K_MEDIUMRAW) < 0 )
	return(-1);
   system("stty raw; stty -echo");
   return(0);
}

LIBRARY void End_Keyboard(void)
{
   (void) ioctl(0, KDSKBMODE, oldmodes);
   system("stty -raw echo");
}

/*
   Wait for a key.
*/

static UInt16 altkeys[26] = {
    0x1e00, 0x3000, 0x2e00, 0x2000, 0x1200, 0x2100, 0x2200, 0x2300,
    0x1700, 0x2400, 0x2500, 0x2600, 0x3200, 0x3100, 0x1800, 0x1900,
    0x1000, 0x1300, 0x1f00, 0x1400, 0x1600, 0x2f00, 0x1100, 0x2d00,
    0x1500, 0x2c00
};

UInt16 WaitForKey(void)
{
  int           add;
  int           keymap;
  unsigned char key;

getkey:
  read(0, &key, 1);

  switch (key&(~UP_BIT)) {
    case Sym_RShift:	if ( key&UP_BIT ) 
				shift_state &= ~RSHIFT_BIT;
			else
				shift_state |= RSHIFT_BIT;
			goto getkey;
    case Sym_LShift:	if ( key&UP_BIT ) 
				shift_state &= ~LSHIFT_BIT;
			else
				shift_state |= LSHIFT_BIT;
			goto getkey;
    case Sym_RControl:
    case Sym_LControl:	if ( key&UP_BIT ) 
				shift_state &= ~CTRL_BIT;
			else
				shift_state |= CTRL_BIT;
			goto getkey;
    case Sym_AltGr:
    case Sym_Alt:  	if ( key&UP_BIT ) 
				shift_state &= ~ALT_BIT;
			else
				shift_state |= ALT_BIT;
			goto getkey;
    case Sym_Caps_Lock:	if ( ! (key&UP_BIT) ) {
				shift_state ^= CAPSLK_BIT;
			}
			goto getkey;
    default:	 break;
  }

  /* We ignore key release events */
  if ( key&UP_BIT )
    goto getkey;

  /* We got a key! :) */
  switch (key)
    {
    case Sym_Tab:
      if (STATE_SHAPS(shift_state))
	return 0x0F00;
      else
	return 0x0009;
    case Sym_Escape:
      return 27;
    case Sym_Delete:
      return 8;
    case Sym_Return:
    case Sym_KP_Enter:
      return 13;
    case Sym_Up:
    case Sym_KP_8:
      return 0x4800;
    case Sym_Down:
    case Sym_KP_2:
      return 0x5000;
    case Sym_Left:
    case Sym_KP_4:
      return 0x4b00;
    case Sym_Right:
    case Sym_KP_6:
      return 0x4d00;
    case Sym_Find:
    case Sym_KP_7:
      return 0x4700;
    case Sym_Prior:
    case Sym_KP_9:
      return 0x4900;
    case Sym_Next:
    case Sym_KP_3:
      return 0x5100;
    case Sym_Select:
    case Sym_KP_1:
      return 0x4f00;
    case Sym_Insert:
    case Sym_KP_0:
      return 0x5200;
    case Sym_Remove:
    case Sym_KP_Period:
      return 0x5300;
    case Sym_F1:
    case Sym_F2:
    case Sym_F3:
    case Sym_F4:
    case Sym_F5:
    case Sym_F6:
    case Sym_F7:
    case Sym_F8:
    case Sym_F9:
    case Sym_F10:
    case Sym_F11:
      add = 0;
      if (STATE_SHAPS(shift_state)) {
	  add += 0x54 - 0x3b;
      }
      if (STATE_CTRL(shift_state)) {
	  add += 0x5e - 0x3b;
      }
      if (STATE_ALT(shift_state)) {
	  add += 0x68 - 0x3b;
      }
      return 0x13b + (key - Sym_F1) + add;
    }

   /* Check for Alt-Key combinations */
   if ( STATE_ALT(shift_state) ) {
      if ((keymaps[NORMAL_MAP][key] >= 'a')&&(keymaps[NORMAL_MAP][key] <= 'z'))
        return(altkeys[keymaps[NORMAL_MAP][key]-'a']);
      else
        goto getkey;
   }

   /* Get the right keymap */
   if ( STATE_CTRL(shift_state) )
	keymap = CONTROL_MAP;
   else if ( STATE_SHAPS(shift_state) )
	keymap = SHIFT_MAP;
   else
        keymap = NORMAL_MAP;

   /* Return the corresponding character */
   return(keymaps[keymap][key]);
}


/*
   Test is a key has been pressed (non-blocking).
*/

Bool IsKeyPressed(void)
{
	fd_set fdset;
	struct timeval tv;

	bzero(&tv, sizeof(tv));
	FD_ZERO(&fdset);
	FD_SET(0, &fdset);
	return(select(64, &fdset, NULL, NULL, &tv));
}


/*
   Get the status of the modifiers: Shift, Alt, Ctrl,... (non-blocking).
*/

UInt16 GetAltKeys(void)
{
  return(shift_state);
}

/* end of file */
