/*
 *  GETKEY.C
 *
 *  (c)Copyright 1990 by Tobias Ferber,  All Rights Reserved.
 */

#include <stdio.h>
#include "getkey.h"


/* Note: Turbo C Version 1.0 can *NOT* read from non-buffered streams */
#if defined(__TURBOC__)
#undef getkey
#include <conio.h>
int getkey(void) { return getch(); }


#elif defined(GNUDOS)

int getkey(void)
/* read from a non-buffered, binary opened console stream */
{
  FILE *con; /* the console stream */
  int key;

  /* you *MUST* open the console in binary mode for DJGPP
   * in order to really get a non-buffered stream via setvbuf()! */

  if( con= fopen(CONSOLENAME,"rb") )
  {
    setvbuf(con,0L,_IONBF,0L);  /* better than setbuf(con,0L); ? */ 

    if(ferror(con))
      clearerr(con);

    fflush(con);

    key= (int)fgetc(con);

    fclose(con);
  }
  else key= stdin ? (int)fgetc(stdin) : 0;

  return key;
}


#elif defined(__MSDOS__)
#include <dos.h>

/*
 * The following stuff is implemented in the c.lib of DJGPP and is
 * Copyright (C) 1991 DJ Delorie, 24 Kirsten Ave, Rochester NH 03867-2954
 *
 *      .globl  _getkey
 * _getkey:
 *      pushl   %ebx
 *      pushl   %esi
 *      pushl   %edi
 *      movb    $0,%ah
 *      int     $0x16
 *      cmpb    $0,%al
 *      jnz     L1
 *      movb    %ah,%al
 *      movb    $1,%ah
 *      jmp     L2
 * L1:
 *      movb    $0,%ah
 * L2:
 *      andl    $0xffff,%eax
 *      popl    %edi
 *      popl    %esi
 *      popl    %ebx
 *      ret
 */

int getkey(void)
/* returns the keycode of the last pressed key using BIOS */
{
  union REGS regs;          /* defined in <dos.h> */
  regs.h.ah= 0x10;          /* get MF-II keycode function # */
  int86(0x16,&regs,&regs);  /* int 16h -> keycode in regs.x.ax */
  return (unsigned short)regs.x.ax;
}


#elif defined(AMIGA)

#include <exec/types.h>
#include <exec/ports.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>

/*
 * Function - SendPacket written by Phil Lindsay, Carolyn Scheppner, and Andy
 * Finkel. This function will send a packet of the given type to the Message
 * Port supplied.
 */

typedef struct StandardPacket StandardPacket;

long
SendPacket(pid, action, args, nargs)
struct MsgPort *pid;    /* process indentifier ... (handler's message port) */
long action;    /* packet type ... (what you want handler to do) */
long *args;     /* a pointer to an argument list */
long nargs;     /* number of arguments in list */
{
    struct MsgPort *replyport;
    StandardPacket *packet;
    long count, *pargs, res1;

    replyport= (struct MsgPort *)CreatePort(NULL, 0L);
    if (!replyport)
	return(0);

    /* Allocate space for a packet, make it public and clear it */

    packet = (StandardPacket *)
      AllocMem(sizeof(StandardPacket), MEMF_PUBLIC | MEMF_CLEAR);

    if (!packet) {
	DeletePort(replyport);
	return(0);
    }
    packet->sp_Msg.mn_Node.ln_Name = (char *) &(packet->sp_Pkt);
    packet->sp_Pkt.dp_Link = &(packet->sp_Msg);
    packet->sp_Pkt.dp_Port = replyport;
    packet->sp_Pkt.dp_Type = action;

    /* copy the args into the packet */

    pargs = &(packet->sp_Pkt.dp_Arg1);      /* address of first argument */
    for (count = 0; count < nargs; count++)
	pargs[count] = args[count];

    PutMsg(pid, &packet->sp_Msg);   /* send packet */

    WaitPort(replyport);
    GetMsg(replyport);

    res1 = packet->sp_Pkt.dp_Res1;

    FreeMem(packet, sizeof(StandardPacket));
    DeletePort(replyport);

    return (res1);
}

int getkey(void)
{
  int key= 0;  /* the one we're going to return */
  BPTR con;    /* BCPL pointer to a struct FileHandle */

  /* We must use Open() here -- not Input() -- because last may
   * be redirected. */

  con= Open( CONSOLENAME, MODE_OLDFILE );

  if( BADDR(con) && IsInteractive(con) ) 
  {
    struct MsgPort *mp;
    long arg[1], res;

    mp = ((struct FileHandle *)(BADDR(con)))->fh_Type;
    arg[0]= -1L;   /* make it RAW */
    res= SendPacket(mp, ACTION_SCREEN_MODE, arg, 1);

    if(res)
    {
      char buffer[4];     /* just to be sure... */
      long actualLength;  /* return value */

      /* The value returned by Read() is the length of the information
       * actually read.  So when 'actualLength' is > 0, then it is the
       * number of characters read.  A value of zero means that the
       * end-of-file has been reached.  Errors are indicated by a value
       * of -1.  Note:  Read is an unbuffered routine. */

      actualLength = Read( con, buffer, (long)sizeof(buffer)-1 );
					  /* 1 extra (for the bugs) */
      if(actualLength > 0)
	key= (int)buffer[0];

      arg[0]= 0L;  /* make it COOKED */
      (void)SendPacket(mp, ACTION_SCREEN_MODE, arg, 1);
    }

    /* Note that we must close 'con' again, whereas Input() must NEVER
     * be closed. */

    Close(con);
  }

  if(!key)
    key= getchar();  /* just to be sure... */

  return key;
}


#elif defined(unix)

#include <curses.h>

int getkey(void)
{
  char c;

#ifdef OBSOLETE

  /* try /dev/tty.  If that doesn't work, use file descriptor 2,
   * which in Unix is usually attached to the screen, but also
   * usually lets you read from the keyboard. */

  int tty= open("/dev/tty", 0);
  if(tty < 0)
    tty= 2;

  { int result;
    do {
      result= read(tty, &c, sizeof(char));
    } while( result != 1 );
  }

  if(tty!=2)
    close(tty);

#else /* POSIX */

  initscr();
  savetty();
  cbreak();
  noecho();
  c= getch();
  resetty();
  endwin();

#endif

  return c;
} 

#endif /* which machine? */


#ifdef TEST
#include <stdlib.h>

main(int argc, char **argv)
{
  int err= 0;

  if(argc==2)
  {
    char *s= argv[1];
    do {
      char c= getkey();
#ifdef DEBUG
      printf("getkey() returned 0x%02x\n",c)
#endif
      for(err=0; s[err]!='\0'; err++)
        if(c==s[err])
          break;
    } while(s[err]=='\0');
  }
  else
  { puts("GETKEY [keys]\n\n"
         "GetKey wartet auf eine der Tasten in 'keys' und gibt den\n"
         "Index der gedrueckten Taste aus 'keys' im Errorlevel zurueck");
  }
#ifdef DEBUG
  printf("errorlevel=%d\n",err);
#endif
  exit(err);
}
#endif /* TEST */
