/* TestKey.c */

/* Aufruf: TestKey <Tastencode> ... <Tastencode> [ANY | ALL]
   <Tastencode> = <Tastennummer> | QUALIFIER | NUMPAD | FKEYS | MISC |
		    RETURN | SPACE
		dabei sind gleichwertig:
		QUALIFIER   96 97 98 99 100 101 102 103
			    (alle Qualifier-Tasten)
		NUMPAD	    15 29 30 31 45 46 47 61 62 63 67 74 90 91 92 93
			    94 (alle Tasten am Ziffernblock)
		FKEYS	    80 81 82 83 84 85 86 87 88 89
			    (alle Funktionstasten)
		RETURN	    67 68 (RETURN und ENTER)
		MISC	    65 66 70 69 76 77 78 79 95
			    (ESC, TAB, BACKSPACE, DEL, HELP und die
			     Cursortasten)
		SPACE	    64 (SPACE)
   <Tastennummer> = <Tastennummer_dezimal> | $<Tastennummer_hexadezimal>
   <Tastennummer_dezimal> = Dezimalzahl zwischen 0 und 103
   <Tastennummer_hexadezimal> = Hexadezimalzahl zwischen 0 und 103

   Funktion: TestKey überprüft, ob die angegebene Taste gedrückt ist
	     oder nicht. Ist sie gedrückt, so wird RETURN_WARN==5 zurück-
	     gegeben, sonst RETURN_OK==0.
	     Bei mehreren Tasten entscheiden die Optionen ANY (default)
	     bzw. ALL über die Verknüpfung:
	     Bei ANY genügt es, daß eine der genannten Tasten gedrückt
	       ist, um RETURN_WARN zu erhalten.
	     Bei ALL müssen alle genannten Tasten gedrückt sein, um
	       RETURN_WARN zu erhalten. 				*/

#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <devices/keyboard.h>
#include <dos/dos.h>
#include <stdlib.h>
#include <strings.h>
#include <stdio.h>
#include <ctype.h>

#define MAXKEY 103
#define BUFFSIZE (MAXKEY/8+1)

#define MODE_ANY FALSE
#define MODE_ALL TRUE

void fail(char *msg) { puts(msg); exit(RETURN_FAIL); }

void check(short n) { if(n<0 || n>MAXKEY) fail("illegal keynumber given"); }

short hexval(char *hex)
{   short prev, new;

    if(*hex==0) fail("unknown keycode '$'");
    for(prev=0;*hex;++hex)
	{
	if(*hex>='0' && *hex<='9')                          new=*hex-'0';
	else if(toupper(*hex)>='A' && toupper(*hex)<='F')   new=*hex-'A'+10;
	else fail("illegal hexdigit given");
	prev=(prev<<4)+new;
	}
    return(prev);
}

#define SET(n) wanted[n>>3]|=(1<<(n&7))

void main(int argc, char *argv[])
{   UBYTE wanted[BUFFSIZE], read[BUFFSIZE];
    short mode,this;
    struct IOStdReq *KBDRequest;
    char *IOError;
    struct MsgPort *Device_Port;

    mode=MODE_ANY;
    for(short cnt=0;cnt<BUFFSIZE;++cnt) wanted[cnt]=0;
    /* Parsing */
    for(short cnt=1;cnt<argc;++cnt)
	{
	if(*argv[cnt]=='$')
	    { /* Angabe als Hex-Zahl */
	    this=hexval(argv[cnt]+1);
	    check(this);
	    SET(this);
	    }
	else if(*argv[cnt]<='0' && *argv[cnt]>='9')
	    { /* Angabe als Dez-Zahl */
	    this=atol(argv[cnt]);
	    check(this);
	    SET(this);
	    }
	else if(!stricmp(argv[cnt],"ANY"))      mode=MODE_ANY;
	else if(!stricmp(argv[cnt],"ALL"))      mode=MODE_ALL;
	else if(!stricmp(argv[cnt],"QUALIFIER")) wanted[12]=255; /* 96..103 */
	else if(!stricmp(argv[cnt],"NUMPAD"))
	    {
	    wanted[1]|=128;	/* 15 */
	    wanted[3]|=224;	/* 29 .. 31 */
	    wanted[5]|=224;	/* 45 .. 47 */
	    wanted[7]|=224;	/* 61 .. 63 */
	    wanted[8]|=8;	/* 67 */
	    wanted[9]|=4;	/* 74 */
	    wanted[11]|=124;	/* 90 .. 94 */
	    }
	else if(!stricmp(argv[cnt],"FKEYS"))
	    {
	    wanted[10]=255;	/* 80 .. 87 */
	    wanted[11]|=3;	/* 88 .. 89 */
	    }
	else if(!stricmp(argv[cnt],"RETURN"))   wanted[8]|=24; /* 67 .. 68 */
	else if(!stricmp(argv[cnt],"MISC"))
	    {
	    wanted[8]|=102;	/* 65, 66, 69, 70 */
	    wanted[9]|=240;	/* 76 .. 79 */
	    wanted[11]|=128;	/* 95 */
	    }
	else if(!stricmp(argv[cnt],"SPACE"))    wanted[8]|=1; /* 64 */
	else fail("illegal keycode given");
	}

    /* Lesen des Ist-Standes: */
    IOError=NULL;
    if(Device_Port=CreatePort(0,0))
	{
	if(KBDRequest=CreateStdIO(Device_Port))
	    {
	    if(OpenDevice("keyboard.device",0,
			  (struct IORequest *)KBDRequest,0)==0)
		{
		KBDRequest->io_Data=read;
		KBDRequest->io_Length=BUFFSIZE;
		KBDRequest->io_Command=KBD_READMATRIX;
		DoIO((struct IORequest *)KBDRequest);
		if(KBDRequest->io_Error)
		    {
		    printf("io_Error==%d\n",KBDRequest->io_Error);
		    IOError="KBD_READMATRIX failed";
		    }
		CloseDevice((struct IORequest *)KBDRequest);
		}
	    else IOError="OpenDevice() on keyboard.device failed";
	    DeleteStdIO(KBDRequest);
	    }
	else IOError="CreateStdIO() failed";
	DeletePort(Device_Port);
	}
    else IOError="CreatePort() failed";
    if(IOError) fail(IOError);

    /* "Vergleich" mit soll: */
    for(short cnt=0;cnt<BUFFSIZE;++cnt) read[cnt]&=wanted[cnt];

    /* Auswertung */
    if(mode==MODE_ANY)
	{ /* mindestens eine Taste */
	for(short cnt=0;cnt<BUFFSIZE;++cnt)
	    if(read[cnt])   exit(RETURN_WARN);
	exit(RETURN_OK);
	}
    else{ /* alle Tasten */
	for(short cnt=0;cnt<BUFFSIZE;++cnt)
	    if(read[cnt]!=wanted[cnt])  exit(RETURN_OK);
	exit(RETURN_WARN);
	}
}
