;/* onekey.c - Execute me to compile me with Lattice 5.04

LC -b0 -cfistq -v -y -j73 onekey.c
Blink FROM LIB:Astartup.obj,handint.o,onekey.o TO onekey LIBRARY LIB:Amiga.lib,LIB:LC.lib SMALLCODE BATCH ND
quit

NOTE - if you use c.o startup, link with LC.lib first, and include stdio.h

*/

#include <exec/types.h>

#include <exec/types.h>
#include <exec/ports.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <exec/tasks.h>
#include <exec/interrupts.h>
#include <exec/devices.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <workbench/startup.h>

#ifdef LATTICE
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/alib_protos.h>
#include <clib/alib_stdio_protos.h>
#include <stdlib.h>
#include <string.h>
int CXBRK(void) { return(0); }  /* Disable Lattice CTRL/C handling */
int chkabort(void) { return(0); }  /* really */
#endif


#define MINARGS 1

UBYTE *vers = "\0$VER: onekey 36.11";
UBYTE *Copyright = 
  "onekey v36.11\nFreely Redistributable, by Carolyn Scheppner - CATS";
UBYTE *usage = "Usage: [run >NIL:] onekey (toggles on/off)\nInput handler for one-key-at-a-time typists\n";

/**********************************************************************/
/* Debug control                                                      */
/*                                                                    */
/* The first define converts any printfs that got in by mistake into  */
/* kprintfs. If you are debuging to the console you can change        */
/* kprintfs into printfs.                                             */
/* The D1(x) define controls debugging in the standard modules. Use   */
/* The D(x) macro for debugging in the app.c module.                  */
/**********************************************************************/
void kprintf(char *,...);

/*
#define DEBUG
*/
#ifdef DEBUG
#define bug kprintf
#define D1(x) x
#define D(x)  x
#else
#define D1(x) ;
#define D(x)  ;
#endif /* NO DEBUG */


BOOL FromWb;

#define KCNT	7
#define CODE_LSHIFT	0x60
#define CODE_RSHIFT	0x61
#define CODE_CONTROL	0x63
#define CODE_LALT	0x64
#define CODE_RALT	0x65
#define CODE_LAMIGA	0x66
#define CODE_RAMIGA	0x67

UWORD kcodes[] = { CODE_LSHIFT, CODE_RSHIFT,
		   CODE_LALT,   CODE_RALT,
		   CODE_LAMIGA, CODE_RAMIGA,
		   CODE_CONTROL };

UWORD kquals[] = { IEQUALIFIER_LSHIFT,   IEQUALIFIER_RSHIFT,
		   IEQUALIFIER_LALT,     IEQUALIFIER_RALT,
		   IEQUALIFIER_LCOMMAND, IEQUALIFIER_RCOMMAND,
		   IEQUALIFIER_CONTROL };


UWORD kqual = 0;

void bye(UBYTE *s, int e);
void cleanup(void);
void unInstall(void);

extern void   HandlerInterface();  /* assembler interface */
struct InputEvent far *myhandler(struct InputEvent *ie1, APTR data);

int    openedID = NULL;
struct MsgPort   *inputPort = NULL;
struct IOStdReq  *inputReq = NULL;

#define MAXIE	16
int ii = 0;
struct InputEvent ups[MAXIE] = {NULL};

UBYTE *progname = "OneKey";
UBYTE *oldname = NULL;

struct Interrupt handlerStuff = {0};


/* C handler routine */

struct InputEvent *myhandler(struct InputEvent *ie1, APTR data)
    {
    struct InputEvent	*ie, *myie, *lastie;
    extern struct Library *SysBase;
    UWORD  kcode;
    int    k;
    BOOL   IsDown, IsUp, SendEm;


    SendEm = FALSE;

    for (ie=ie1; ie; lastie=ie, ie=ie->ie_NextEvent)
	{
	if (ie->ie_Class==IECLASS_RAWKEY)
	    {
	    kcode = ie->ie_Code & (~IECODE_UP_PREFIX);
	    for(k=0,IsUp=FALSE,IsDown=FALSE; k<KCNT; k++)
		{
		if(kcode == kcodes[k])
		    {
		    if(ie->ie_Code & IECODE_UP_PREFIX) IsUp = TRUE;
		    else IsDown = TRUE;
		    break;
		    }
		}

	    D(bug("got code $%lx, IsUp=%ld, IsDown=%ld\n",kcode,IsUp,IsDown));
	    if(IsDown)	/* is a gathered key - gather the quals */
	    	{
	    	D(bug("got down  k=%ld\n",k));
	    	kqual |= kquals[k];
	   	}
	    else if(IsUp)	/* release of a gathered key - we'll eat it */
	    	{
	    	D(bug("got up  k=%ld\n",k));

	    	myie = &ups[ii];		/* use my next empty event */
		*myie = *ie;			/* clone this key up event  */
		if(ii < (MAXIE-1)) ii++;	/* set up for next */
	    	ie->ie_Class = IECLASS_NULL;	/* null out the real event */
	    	}
	    else if(kqual)	/* got a non-qualifier key and we have gathered */
	    	{
	    	D(bug("got key  code=%lx, gathered=$%lx\n",kcode,kqual));
	    	ie->ie_Qualifier |= kqual;	/* apply the qualifiers */

	    	/* if key release, set up fake saved keyups now too, clear gather */
	    	if(ie->ie_Code & IECODE_UP_PREFIX)
		    {
		    /* update time stamps and link our list */
		    for(k=0; k<ii; k++)
			{
			ups[k].ie_TimeStamp.tv_secs  = ie->ie_TimeStamp.tv_secs;
			ups[k].ie_TimeStamp.tv_micro = ie->ie_TimeStamp.tv_micro+k;
			if(k<(ii-1)) ups[k].ie_NextEvent = &ups[k+1];
			else ups[k].ie_NextEvent = NULL;
			}
		    SendEm = TRUE;

		    kqual = 0;
		    }
		}
	    }
	}
    if(SendEm)
	{
	lastie->ie_NextEvent = &ups[0];
	ii = 0;
	}
    return(ie1);
    }


#define MYNAME "cas_cbm_onekey"

void main(int argc, char **argv)
    {
    struct Task *task;
    int  error;

    FromWb = argc ? FALSE : TRUE;

    if(((argc)&&(argc<MINARGS))||(argv[argc-1][0]=='?'))
	{
	printf("%s\n%s\n",Copyright,usage);
	bye("",RETURN_OK);
	}

    if(task=FindTask(MYNAME))
	{
	if(!FromWb) printf("Signalling OneKey to remove itself\n");
	Signal(task,SIGBREAKF_CTRL_C);
	bye("",RETURN_OK);
	}

    task = FindTask(NULL);
    oldname = task->tc_Node.ln_Name;
    task->tc_Node.ln_Name = MYNAME;


   /* Get ready to add our handler */
   if(!(inputPort=(struct MsgPort *)CreatePort(0,0)))
      bye("Can't create port\n",RETURN_FAIL);

   if(!(inputReq=(struct IOStdReq *)CreateStdIO(inputPort)))
      bye("Can't create IORequest\n",RETURN_FAIL);



   handlerStuff.is_Data = NULL;              /* no passed data pointer */
   handlerStuff.is_Code = HandlerInterface;  /* assem entry */
   handlerStuff.is_Node.ln_Pri = 51;         /* above Intuition */

   if(error = OpenDevice("input.device",0,inputReq,0))
      bye("Can't open input device\n",RETURN_FAIL);
   openedID = 1;

   inputReq->io_Command = IND_ADDHANDLER;
   inputReq->io_Data = (APTR)&handlerStuff;
      
   DoIO(inputReq);

   if(argc) printf("%s installed\n",progname);

   Wait(SIGBREAKF_CTRL_C);

   bye("",0L);
   }


void bye(UBYTE *s, int e)
   {
   if(!FromWb) printf(s);
   unInstall();
   exit(e);
   }

void unInstall()
   {
   if (openedID)
      {
      /* remove the handler from the chain */
      inputReq->io_Command = IND_REMHANDLER;
      inputReq->io_Data = (APTR)&handlerStuff;
      DoIO(inputReq);
      /* close the input device */
      CloseDevice(inputReq);
      }

   /* delete the IO request, port, FreeSignal */
   if(inputReq)		DeleteStdIO(inputReq);
   if(inputPort)	DeletePort(inputPort);
   if(oldname)		FindTask(NULL)->tc_Node.ln_Name = oldname;
   if((openedID)&&(!FromWb)) printf("%s removed\n",progname);
   }




