/*      kb_input.c                                       07.12.93
/*!
/  --------------------------------------------------------------
/  Copyright (C) 1993: Michael Braun
/                      Kaetinger Muehlenweg 103 A
/                      D-28816 Stuhr
/  --------------------------------------------------------------
/
/  keyboard input subroutines, operating system dependant
/
*/

/****************************************************************
*                                                               *
*  BENUTZTE UNTERPROGRAMME (C LIBRARY)                          *
*                                                               *
****************************************************************/

#include "config.h"
#include "standard.h"
#include "kb_input.h"


/****************************************************************
*                                                               *
*  BENUTZTE UNTERPROGRAMME / GLOBALE VARIABLEN                  *
*                                                               *
****************************************************************/


#if (ACT_OP_SYSTEM == OS_9)

   #ifndef fileno
      #define fileno(p)       ((p)->_fd)
   #endif

#endif


#define PATH_0   fileno(stdin)   /* standard input */

#define input_redirected 0
#define check_end_of_file(a) (a)

/****************************************************************
*                                                               *
*  ENDE DER DEKLARATIONEN                                       *
*                                                               *
****************************************************************/

/* -FF-  */

/*****************************************/
/*                                       */
/*  operating system independant region  */
/*                                       */
/*****************************************/

/* -FF-  */

int key_pressed (void)
{

   if (loc_key_pressed ()) return 1;

   return 0;   /* nothing found */

}   /* key_pressed */

/* -FF-  */

int loc_get_1_key (void)
{
int key;

   key = loc_get_1_int ();

#if 1
   if (key == 0x0d) key = EOLN;
#endif

   return key;

}  /* loc_get_1_key */

/* -FF-  */

int get_1_key (int check_macro)
{
int key;

/*
  Diese Routine liefert einen Tastendruck zurueck, der von verschiedenen
  Quellen herruehren kann :
    - normal keyboard (bediener-eingabe)

  Die Quellen werden in dieser Reihenfolge abgecheckt, und bei Vorliegen
  eines "Tastendrucks" wird dieser ans rufende Programm zurueckgeliefert
  und ggf. im Buffer geloescht.
*/


/* 5.) read 1 key by user input */
   key = loc_get_1_key ();

   return key;    /* ==> */

}  /* get_1_key */

/* -FF-  */

/* fallunterscheidung */

#if (ACT_OP_SYSTEM == MS_DOS)

/*****************************************/
/*                                       */
/*           PC - MF2-Keyboard           */
/*                                       */
/*****************************************/

/*****************************************/
/*                                       */
/*           MS_DOS region               */
/*                                       */
/*****************************************/

/************************/
/*  n msec's delay      */
/************************/

void sleep_msec (int time_delay)
{

#if 0   /* LSB = 1 sec */

time_t time_start,time_act;

   time_delay /= 1000;    /* LSB: msec --> sec */

   for ( time(&time_start) , time(&time_act) ;
         (int)(time_act - time_start) < time_delay  ;
         time(&time_act))
   {
   }

#else   /* LSB = 10 msec */

struct dostime_t time_start, time_act;
int msec10_start, msec10_act;

   time_delay /= 10;    /* LSB: msec --> msec10 */

   _dos_gettime (&time_start);
   msec10_start = (int) time_start.second * 100 +
                  (int) time_start.hsecond;
   for (;;)
   {
      _dos_gettime(&time_act);
      msec10_act = (int) time_act.second * 100 +
                   (int) time_act.hsecond;
   
      if ((((msec10_act - msec10_start) + 6000) % 6000) >= time_delay)
         break;     
   }

#endif

   return;
}  /* sleep_msec */


/************************/
/*  is key pressed ?    */
/************************/

#define INPUT_VIA_BIOS 1  /* muss auf 1 stehen, wenn input redirection */

int loc_key_pressed (void)
{
/* return true, if key is pressed, else false */

   if (input_redirected) return 0;

#if INPUT_VIA_BIOS
   return _bios_keybrd(_KEYBRD_READY);
#else
   return kbhit ();
#endif
}

/* -FF-  */

/************************/
/*  get 1 key stroke    */
/************************/

static int loc_get_1_int (void)
{
/* This functions returns special keys (function, cursor keys, ...) */
/* in one (modified) int.   */

int key;
#define INPUT_VIA_BIOS 1  /* muss auf 1 stehen, wenn input redirection */

   if (input_redirected)
   {                          /* input from file (stdin) */
      key = getchar ();

      return check_end_of_file (key);
   }
   else
   {                          /* input from keyboard direct */
   
#if INPUT_VIA_BIOS
   
   /* special keys are transferred as MSB            */
   /* in return value of _bios_keybrd, with LSB = 0. */
   
      key = _bios_keybrd(_KEYBRD_READ);
   
      if ((key & 0xff) != 0)
         return (key & 0xff);       /* normal key, return LSB direct (positiv) */
      else
         return (((key >> 8) & 0xff) - 256);  /* special key,
                                                 return MSB modified (negativ) */
   
#else
   
   /* special keys (function, cursor keys, ...) are transferred as */
   /* a sequence of two ints (with the first int = 0).             */
   
      key = getch();
   
      if (key != 0)
         return key;              /* normal key, return direct (positiv) */
      else
         return (getch() - 256);  /* special key, return modified (negativ) */
   
#endif
   }

}  /* loc_get_1_int */

/* -FF-  */

/************************/
/*  echo on / off       */
/************************/


void kb_echo_off (void)
{
   return;
}  /* kb_echo_off */


void kb_echo_on (void)
{
   return;
}  /* kb_echo_on */

#else   /* MS_DOS */

/* -FF-  */

/*****************************************/
/*                                       */
/*    VT 100 / VT 220 - Terminal         */
/*                                       */
/*****************************************/


/************************/
/*  get 1 key stroke    */
/************************/

#define MAX_SEQU 10
int key_sequence [MAX_SEQU];
static int sequ_ind;


int *get_key_sequence (void)
{
   return key_sequence;
}  /* get_key_sequence */


static int loc_get_1_int (void)
{
/* translation from <esc> sequences in one single (negativ) int */

/* special keys (function, cursor keys, ...) are transferred as */
/* <esc> sequence with variable no of chars.                    */
/* This functions returns special keys in one (modified) int.   */

int ii, key, result, value;

static int esc_buff;   /* if a single <esc> is followed by a sequence,  */
                       /* this event must be handled in special manner. */ 


/* NEU ! output buffer leerraeumen */
   fflush (stdout);

/* clear test buffer */
   sequ_ind = 0;
   memset (key_sequence, 0, sizeof(key_sequence));


/* is there still a char in the queue ? */
   if (esc_buff)
   {
      key = esc_buff;        /* take it */
      esc_buff = 0;          /* clear buffer */
   }
   else
   {
      key = get_1_byte ();   /* get new byte from stdin */
   }


/* EOF bei input redirection ? */
   key = check_end_of_file (key);


/* <esc> handling */
   if ((key != 0x1b) ||        /* is it no <esc> ? */
       (input_redirected))     /* read from file ? */
   {
      return key;              /* send directly */ 
   }
   else                        /* check for <esc> sequence */
   {   
   /* are there characters following immediately ? */
      result = get_1_byte_with_timeout (ESC_WAITTIME);

      if (result < 0)           /* timeout ? */
      {                         /* yes : single <esc> */
         return key;            /* send it ! */
      }
      else                      /* no : <esc> sequence */
      {
         key = result;          /* get following byte */
         switch (key)
         {
            case '[':
               key = get_1_byte ();
               switch (key)
               {
                  case 'A': return KEY_UP   ; break;   /* cursor keys */
                  case 'B': return KEY_DOWN ; break;
                  case 'C': return KEY_RIGHT; break;
                  case 'D': return KEY_LEFT ; break;

                  case '1':
                  case '2':
                  case '3':
                  case '4':
                  case '5':
                  case '6':
                     value = key - '0';    /* build a 1- or 2-digit-number */
                     for (ii = 0 ; ii < 2 ; ii++)  /* read next bytes */
                     {
                        key = get_1_byte ();
                        if (key == '~') break;     /* last char in sequence */
                        value = 10 * value + (key - '0');
                     }

                     switch (value)
                     {
                        case  1: return KEY_HOME ; break;
                        case  2: return KEY_INS  ; break;
                        case  3: return KEY_DEL  ; break;
                        case  4: return KEY_END  ; break;
                        case  5: return KEY_PGUP ; break;
                        case  6: return KEY_PGDN ; break;

                        case 17: return KEY_F6   ; break;
                        case 18: return KEY_F7   ; break;
                        case 19: return KEY_F8   ; break;
                        case 20: return KEY_F9   ; break;
                        case 21: return KEY_F10  ; break;
                        case 23: return KEY_F11  ; break;
                        case 24: return KEY_F12  ; break;
                        
                        case 25: return KEY_F13  ; break;
                        case 26: return KEY_F14  ; break;
                        case 28: return KEY_HELP ; break;
                        case 29: return KEY_DO   ; break;
                        
                        case 31: return KEY_F17  ; break;
                        case 32: return KEY_F18  ; break;
                        case 33: return KEY_F19  ; break;
                        case 34: return KEY_F20  ; break;

                        default: return INVALID  ; break;
                     }

                  default:
                     return INVALID;
                     break;                  
               }
               break;

            case 'O':           /* <PF> keys */
               key = get_1_byte ();
               switch (key)
               {
                  case 'P': return KEY_PF1; break;
                  case 'Q': return KEY_PF2; break;
                  case 'R': return KEY_PF3; break;
                  case 'S': return KEY_PF4; break;

                  default:  return INVALID; break;
               }
               break;

            default:                  /* <esc> followed by any other char */
               esc_buff = key;        /* may also be a new <esc> sequence */
               return 0x1b;
               break;
         }
      }
      
      
   }

}  /* loc_get_1_int */

#endif

/* -FF-  */

/*****************************************/
/*                                       */
/*    Other Operating Systems            */
/*                                       */
/*  This is the region, to adapt the     */
/*  keyboard driver to a new operating   */
/*  system. Only 5 small function have   */
/*  to be modified:                      */
/*                                       */
/*****************************************/

#if 0

|                |                kb_input.c                    |
|                |                                              |
|  application   |   translate vt220   |   op-system specific   |
|                |       (fix)         |    (to be adapted)     |

               ---------------------------> kb_echo_off ()
               ---------------------------> kb_echo_on  ()
                                       
               <-----> get_1_key () <--+--> get_1_byte  ()
                                       |
                                       +--> get_1_byte_with_timeout ()
                                       |
               <-------------------->  +--> loc_key_pressed ()

global functions :
   kb_echo_off ()
   kb_echo_on  ()
   key_pressed     ()
   loc_key_pressed ()
   get_1_key     ()
   loc_get_1_int ()

local functions :
   get_1_byte ()
   get_1_byte_with_timeout ()

#endif                                                        

/*****************************************/
/*                                       */
/*           OS_9 region                 */
/*                                       */
/*****************************************/

#if (ACT_OP_SYSTEM == OS_9)

#define CODE     0                 /* for getstat, setstat       */

static struct _sgs old;   /* buffers for echo on/off */
static struct _sgs new;


/************************/
/*  n msec's delay      */
/************************/

void sleep_msec (int msec)
{
   msec /= TIMER_TICK;    /* scale: msec --> timer ticks (LSB = 1 sec) */
   msec = max (1, msec);  /* minimal 1 tick */
   tsleep (msec);         /* do it */

   return;
}  /* sleep_msec */


/************************/
/*  is key pressed ?    */
/************************/

int loc_key_pressed (void)
{
/* return true, if key is pressed, else false */

   if (input_redirected) return 0;

   if (_gs_rdy (PATH_0) >= 0)
      return 1;
   else
      return 0;
}


/************************/
/*  get 1 single byte   */
/************************/

static int get_1_byte (void)
{
char byte1;
int  num;

   num = read (PATH_0, &byte1, 1);

   if ((input_redirected) && (num == 0))
   {
      key_sequence[sequ_ind++] = EOF;
      return EOF;
   }
   else
   {
      key_sequence[sequ_ind++] = (int) byte1;
      return byte1;
   }

}  /* get_1_byte */


/************************/
/*  get with timeout    */
/************************/

static int get_1_byte_with_timeout (int wait_time)
{
/* if key is pressed before wait_time elapsed, key is returned, else -1 */
/* wait_time : LSB = 1 msec */
/* special cases: wait_time = 0 : no wait */
/*                wait_time < 0 : wait forever */

int time;

/* wait forever ? */
   if (wait_time < 0)  return get_1_byte ();

/* is key already there ? */
   if (loc_key_pressed ()) return get_1_byte ();
   
/* wait given time */
   for (time = 0 ; time < wait_time ; time += TIMER_TICK)
   {
      sleep_msec (10);
      if (loc_key_pressed ()) return get_1_byte ();
   }

   return -1;  /* timeout */
}  /* get_1_byte_with_timeout */

/* -FF-  */

/************************/
/*  echo on / off       */
/************************/

static int on_off;

void kb_echo_off (void)
{
   if (input_redirected) return;

   if (on_off == 0)
   {
   /* get current settings */
      getstat (CODE, PATH_0, &old);          /* saved  in old */
      memcpy (&new, &old, sizeof (new));     /* copied to new */
   
   /* set options */
      new._sgs_echo  = 0;
      new._sgs_pause = 0;
      new._sgs_eorch = 0;
      new._sgs_eofch = 0;

      new._sgs_alf   = 0;     /* no auto <lf> */

      new._sgs_kbich = 0;     /* ^C */
      new._sgs_kbach = 0;     /* ^E */
      new._sgs_xon   = 0;     /* ^S */
      new._sgs_xoff  = 0;     /* ^Q */

   /* write back new settings */
      if (setstat (CODE, PATH_0, &new) != -1)
         on_off = 1;  /* o.k. */
   }

   return;
}  /* kb_echo_off */


void kb_echo_on (void)
{
   if (input_redirected) return;

   if (on_off == 1)
   {
   /* write back old settings */
      if (setstat (CODE, PATH_0, &old) != -1)
         on_off = 0;
   }

   return;
}  /* kb_echo_on */

#endif  /* OS_9 */

/* -FF-  */

/*****************************************/
/*                                       */
/*           UNIX region                 */
/*                                       */
/*****************************************/

#if (UNIX)

static struct termio tbufsave;
static struct termio tbuf;

/* -FF-  */

/* error display in status line (given text + errno) */

void syserr (char *text)
{
char ges_line [81];
char err_disp [30];

/* build complete line */
   strncpy (ges_line, text, (sizeof(ges_line) - 1));
   sprintf (err_disp, ", errno = %d", errno);

/* check for buffer overflow */
   if ((strlen(ges_line) + strlen(err_disp)) < sizeof(ges_line))
      strcat (ges_line, err_disp);

/* display */
   printf ("\n syserr: %s \a\n", ges_line);
   fflush (stdout);

   sleep_msec (1000);   /* 1 sec. pause */

   return;
}  /* syserr */

/* -FF-  */


/************************/
/*  n msec's delay      */
/************************/

void sleep_msec (int msec)
{
   msec /= TIMER_TICK;    /* scale: msec --> timer ticks (LSB = 1 sec) */
   msec = max (1, msec);  /* minimal 1 tick */
   sleep (msec);          /* do it */

   return;
}  /* sleep_msec */


/************************************************************************/
/* set the terminal I/O blocking on and off                             */
/************************************************************************/

static void setblock (int fd, int on)
{
   static int blockf, nonblockf;
   static int first = 1;
   int flags;

   if (first)
   {
      first = 0;
      if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
         syserr("fcntl");
      blockf = flags & ~O_NDELAY;     /* O_NDELAY off */
      nonblockf = flags | O_NDELAY;   /* O_NDELAY on  */
   }

   if (fcntl(fd, F_SETFL, on ? blockf : nonblockf) == -1)
      syserr("fcntl2");
}  /* setblock */

/************************/
/*  is key pressed ?    */
/************************/

#define EMPTY '\0'
static char cbuf[16] = "\0";

int loc_key_pressed (void)
{
/* return true, if key is pressed, else false */

   if (input_redirected) return 0;

   if (*cbuf != EMPTY)
      return (1);

   setblock (PATH_0, 0);
   switch (read (PATH_0, cbuf, 1))
   {
      case -1:
         errno = 0;
         return (EMPTY);

      case  0:
         return (EMPTY);          /* could be EOF too */

      default:
         cbuf[1]=EMPTY;
         return 1;
   }
}  /* loc_key_pressed */


/************************/
/*  get 1 single byte   */
/************************/

static int get_1_byte (void)
{
   char c;

   if (*cbuf != EMPTY)
   {
      c = *cbuf;
      strcpy(cbuf,&cbuf[1]);  /* shift buffer 1 charact. down */
      key_sequence[sequ_ind++] = (int) c;
      return(c);
   }

   setblock (PATH_0, 1);
   switch (read (PATH_0, &c, 1))
   {
      case -1: 
         if (errno == EINTR)
         {
            errno = 0;
            key_sequence[sequ_ind++] = -1;
            return (-1);
         }
         else
         {
            syserr("read 1 stdin");
         }
         /* fall through */
         
      case 0:
         key_sequence[sequ_ind++] = -1;
         return (-1);             /* must be EOF  */
         
      default:
         key_sequence[sequ_ind++] = (int) c;
         return (c);
   }

}  /* get_1_byte */

/* -FF-  */

/************************/
/*  get with timeout    */
/************************/

static jmp_buf env;

static int get_1_byte_with_timeout (int w_time)
{
char c;
int nr, tms, jmpret;

   tms = max(1, ((w_time + (TIMER_TICK-1)) / TIMER_TICK));

   if (w_time < 0 )
   {   
       c = get_1_byte();
       key_sequence[sequ_ind++] = (int) c;
       return (c);
   }

   if (*cbuf != EMPTY)
   {
       c = *cbuf;
       strcpy (cbuf, &cbuf[1]);  /* shift buffer 1 charact. down */
       key_sequence[sequ_ind++] = (int) c;
       return(c);
   }

   setblock (PATH_0, 1);          /* das fehlen dieser anweisung verursachte */
                                  /* die probleme unter unix svr4 !!         */

   signal (SIGALRM, key_brk);     /* set signal handling       */

   alarm (tms);                   /* set timeout: LSB 1 sec !! */
   jmpret = setjmp (env);

   if (jmpret == 0)                  /* auf unix svr4 ueberfluessig, da bei */
      nr = read (PATH_0, cbuf, 2);   /* SIGALRM die read function abgebro-  */
   else                              /* chen wird (-1), bei ultrix nicht !! */
      nr = jmpret;

   alarm (0);                     /* reset timeout */

   switch (nr)
   {
      case -1:
         /* key_sequence[sequ_ind++] = -1; */
         return(-1);
          
      case 0:
         key_sequence[sequ_ind++] = -1;
         return (-1);           /* EOF */
 
      default:
         cbuf[nr] = EMPTY;
         c = *cbuf;
         strcpy( cbuf, &cbuf[1] );  /* shift buffer */
         key_sequence[sequ_ind++] = (int) c;
         return(c);
   }
}  /* get_1_byte_with_timeout */


/* dummy routine fuer alarm handling */
static void key_brk (void)
{
   longjmp (env, -1);
}

/* -FF-  */

/************************/
/*  echo on / off       */
/************************/

static int on_off;

void kb_echo_off (void)
{
   if (input_redirected) return;

   if (on_off == 0)
   {
      on_off = 1;

      if (ioctl (PATH_0, TCGETA, &tbuf) == -1)
             syserr("ioctl get termio");
   
      tbufsave = tbuf;                        /* save the old structure */
   
      setraw (1, 1);
   }

   return;

}  /* kb_echo_off */

static void setraw (uchar c_num, uchar t_msec10)
{

/* INLCR : map (not map) NL to CR                               */
/* ICRNL : map (not map) CR to NL                               */
/* IUCLC : map (not map) upper case to lower case on input      */
/* ISTRIP: strip (not)   input characters to seven bits         */

   tbuf.c_iflag &= ~(INLCR | ICRNL | IUCLC | ISTRIP | BRKINT);
   tbuf.c_iflag |=  (IXON | IXOFF);    /* handshake in both (!) directions */
   tbuf.c_oflag &= ~OPOST;
   tbuf.c_lflag &= ~(ICANON | ISIG | ECHO);
   tbuf.c_cc[4] = c_num;      /* MIN */
   tbuf.c_cc[5] = t_msec10;       /* TIME */
   if (ioctl (PATH_0, TCSETA, &tbuf) == -1)
         syserr("ioctl set termio");

   return;
}  /* set_raw */


void kb_echo_on (void)
{
   if (input_redirected) return;
   
   if (on_off == 1)
   {
      on_off = 0;

      if (ioctl (PATH_0, TCSETAF, &tbufsave) == -1)
         syserr("ioctl reset term");
   }

   return;
}  /* kb_echo_on */

#endif  /* UNIX */

/* -FF-  */

/* Modification History */
/* 21.10.92 - file erzeugt */
/* 01.12.92 - vt100 ansteuerung  */
/* 04.12.92 - keyboard_echo on/off */
/* 05.12.92 - pause_msec () */
/* 21.12.92 - get_1_key (): fflush (stdout); bei VT_100 */
/* 01.01.93 - loc_get_1_key () */
/* 03.01.93 - loc_get_1_key () public (for mbedit.c) */
/* 15.08.93 - loc_key_pressed: #if INPUT_VIA_BIOS */
/* 12.09.93 - show_status_line_2 (..., ignore_batch) */
/* 14.09.93 - get_1_key(), special case <cr><lf> */
/* 30.09.93 - TEST_EOLN */
/* 08.10.93 - loc_get_1_key() --> loc_get_1_int() */
/* 04.11.93 - TEST_EOLN entfaellt */
/* 07.12.93 - down strip for kb_test.c */
