/*
 * InputMonitor
 *
 * di Romano Tenca
 *
 * Un programma Public Domain per visualizzare gli InputEvent.
 * Ispirato a input.dev di R. Peck (Fish Disk n.5).
 *
 * Compilare con Lattice:
 *
 *  lc -v -y InputMonitor.c
 *
 * Linking:
 *
 *  versione 5.0:
 *  blink from c.o InputMonitor.o to InputMonitor
 *  lib lib:lc.lib SC SD
 *
 *  versioni precedenti:
 *  blink from c.o interfacciahandler.o InputMonitor.o to InputMonitor 
 *  lib lib:lc.lib lib:amiga.lib SC SD
 * 
 * lunghezza TAB: 4 
 */

#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 <devices/input.h>
#include <exec/devices.h>
#include <devices/inputevent.h>
#include <proto/exec.h>

/* Se si definisce questo simbolo il monitor funzionera' anche da	*/
/* accelleratore del mouse */
/*
#define ACCELLERATORE
*/

#define abs(x)	((x)>=0?(x):(-x))
#define min(x,y)	((x)<(y)?(x):(y))

#define MILIONE 1000000
#define ESCKEYUP 0xc5
#define HELPKEYUP 0xdf

/* Questo simbolo definisce la priorita' del nostro handler: se e' maggiore	*/
/* di 50 intercettera' gli eventi prima di Intuition; se e' inferiore li	*/
/* vedra' dopo l'elaborazione: in questo caso sara' disponibile una gamma	*/
/* molto piu' ampia di eventi (riguardanti menu, window, gadget...).	*/

#define HANDLERPRI 52

#ifndef LATTICE_50

/* Necessario per le versioni del Lattice senza la keyword __asm	*/
extern interfacciahandler();

#endif

void stampaev(struct InputEvent *);
void uscita(UBYTE codice);

/* Variabili Globali */

struct MsgPort *mioport;
struct IOStdReq *inputIOSR;
struct Interrupt infohandler;
short dastampare, numcopia;
int oldsecs,oldmicro;
struct Task *maintask;
long numerosignal=-1;
long masksignal;
int openerr=-1;
int ioerr=-1;

/* Questo è l'array che verrà usato per immagazzinare una copia				*/
/* degli eventi di input in attesa di stampa. L'array è circolare: quando	*/
/* l'indice raggiunge il valore MAXNUMEV-1 viene azzerato, riutilizzando	*/
/* gli elementi che dovrebbero essere gia' stampati sul video; la macro che */
/* segue viene usata per incrementare l'indice dell'array */

#define MAXNUMEV 100
struct InputEvent multiev[MAXNUMEV];
#define INCR(x) (x)=( (x) == (MAXNUMEV-1) ? 0 : (x+1) )

char * msgerrore[]=
	{
	"Tutto Ok.",
	"Err: 1 CreatePort",
	"Err: 2 CreateStdIO",
	"Err: 3 OpenDevice",
	"Err: 4 AllocSignal",
	"Err: 5 DoIO"
	};

/****************************************************************************/

/* Questo è l'handler vero e proprio: verrà chiamato dal task 				*/
/* dell'input.device, di cui userà tutti i parametri, fra cui lo stack		*/


#ifdef LATTICE_50

/* Se la versione del Lattice e' la 5.0 si puo' usare la keyword __asm		*/

struct InputEvent * 
__asm inputhandler(register __a0 struct InputEvent *ev, register __a1 APTR dati)

#else

struct InputEvent * 
inputhandler(struct InputEvent *ev, APTR dati)

#endif

{
register struct InputEvent *localev=ev;

/* Andiamo in Forbid solo per impedire a main() di leggere		*/
/* l'array mentre noi vi ricopiamo i dati						*/

Forbid();

do
	{
	/* Evitiamo di stampare gli eventi che provengono dal timer.device		*/
	/* perché troppo frequenti (10 al secondo)	*/

	if(localev->ie_Class == IECLASS_TIMER) continue;

#ifdef ACCELLERATORE

	/* Un semplice esempio di accelleratore del mouse: praticamente raddoppia	*/
	/* il valore del movimento quando e' superiore a SOGLIA. Perche appaia		*/
	/* nell'eseguibile occorre che ACCELLERATORE venga definito con una #define	*/

	#define SOGLIA 8

	if(localev->ie_Class==IECLASS_RAWMOUSE && localev->ie_Code==IECODE_NOBUTTON)
		{
		if( abs(localev->ie_X) > SOGLIA ) localev->ie_X <<= 1; 
		if( abs(localev->ie_Y) > SOGLIA ) localev->ie_Y <<= 1; 
		}
#endif

	/* Se esistono MAXNUMEV eventi in attesa di stampa scarichiamo i nuovi	*/
	/* eventi in arrivo, che non saranno visualizzati						*/

	if(dastampare==MAXNUMEV) continue; 

	/* Ricopiamo tutti i campi*/

	multiev[numcopia].ie_Class = localev->ie_Class;
	multiev[numcopia].ie_SubClass = localev->ie_SubClass;
	multiev[numcopia].ie_Code =  localev->ie_Code;
	multiev[numcopia].ie_Qualifier = localev->ie_Qualifier;
	multiev[numcopia].ie_X = localev->ie_X;
	multiev[numcopia].ie_Y = localev->ie_Y;
	multiev[numcopia].ie_TimeStamp.tv_secs  = localev->ie_TimeStamp.tv_secs;
	multiev[numcopia].ie_TimeStamp.tv_micro = localev->ie_TimeStamp.tv_micro;

	/* Aumentiamo il contatore del numero di eventi da stampare e l'indice	*/
	/* dell'array */

	dastampare++;
	INCR(numcopia);

	/* Avvertiamo main() che un nuovo evento e' stato aggiunto all'array	*/

	Signal(maintask,masksignal);

	/* Ripetiamo il loop se esiste un altro evento collegato al presente	*/

	} while(localev = localev->ie_NextEvent);

Permit();
return(ev);	

}

/* Fine dell'input handler */

/***************************************************************************/

main()
{
short numstampa=0;

/* Creiamo il Port e lo IOStdReq	*/

mioport = CreatePort((char *)0,0L);
if( !mioport ) uscita(1);

inputIOSR = CreateStdIO(mioport);     
if( !inputIOSR ) uscita(2);

/* Apriamo l'input.device */

openerr = OpenDevice("input.device",0,inputIOSR,0);
if(openerr) uscita(3);

/* Allochiamo un segnale per le comunicazioni fra l'handler e la main() */

numerosignal=AllocSignal(-1);
if(numerosignal==-1) uscita(4);

masksignal=1<<numerosignal;

/* Troviamo l'indirizzo del task di main() che servira' all'handler per	*/
/* inviarci un segnale*/

maintask=FindTask(0L);

/* Diamo qualche informazione */

printf("\nInputMonitor\ndi Romano Tenca\n\n");
printf("Si prema ESC per finire.\n");
printf("E' meglio disattivare la finestra!!\n\n");
printf("Significato delle abbreviazioni:\n");
printf("Cl=ie_Class  Sc=ie_SubClass  c=ie_Code  q=ie_Qualifier\n");
printf("t=tv_secs e tv_micro i=intervallo (secs e micro dall'ultimo evento)\n");
printf("u=Code e Qualifier dell'ultimo tasto  pu=idem per il penultimo tasto\n");
printf("x=ie_X  y=ie_Y  a=ie_EventAddress\n");
printf("Tutti i valori sono esadecimali tranne x e y.\n");

/* Inizializziamo la struct Interrupt con le informazioni sul nostro handler*/
 
#ifdef LATTICE_50
infohandler.is_Code = (void(*)())inputhandler;
#else
infohandler.is_Code = (void(*)())interfacciahandler;
#endif

infohandler.is_Data = (APTR)NULL;
infohandler.is_Node.ln_Pri = HANDLERPRI;

/* Inseriamo nello IOStdReq il comando e il puntatore alla struct Interrupt	*/

inputIOSR->io_Command = IND_ADDHANDLER;
inputIOSR->io_Data = (APTR)&infohandler;
		
/* Inviamo la richiesta all'input.device */
 
ioerr = DoIO(inputIOSR);
if(ioerr) uscita(5);
 
/* Ora possiamo iniziare il loop che si preoccupa di stampare gli eventi 	*/
/* copiati dall'handler nell'array multiev[]. Si noti che se il numero		*/
/* di eventi è eccessivo, l'handler scarichera' i nuovi eventi in arrivo.	*/
/* Si può diminuire questo rischio, aumentando il numero di elementi di		*/
/* multiev[], definito in MAXNUMEV, che di default è pari a 100 			*/
/* Il loop e' infinito, se no esce solo quando si trova un evento con 		*/
/* ESCKEYUP. */

for(;;)
	{

	/* Attendiamo dall'handler un segnale: esso indica che un nuovo InputEvent	*/ 
	/* e' stato copiato in multiev[] */

	Wait(masksignal);

	/* La variabile "dastampare" viene incrementata dal nostro handler ogni	*/
	/* volta che un nuovo evento viene aggiunto all'array e contiene il		*/
	/* numero di eventi in attesa di stampa	*/

	while(dastampare)
		{
		/* Se è stato rilasciato il tasto ESC il programma termina.			*/
		/* "numstampa" contiene il numero dell'elemento dell'array che va	*/
	    /* ancora stampato, parte da 0 e giunge fino MAXNUMEV-1				*/

		if(multiev[numstampa].ie_Class == IECLASS_RAWKEY && 
		   multiev[numstampa].ie_Code  == ESCKEYUP )
		   uscita(0);


		/* Abbiamo trovato un evento da stampare */

		Forbid();
		stampaev(&multiev[numstampa]);

		/* Decrementiamo il contatore degli eventi da stampare	*/

		dastampare--;
		Permit();

		/* Incrementiamo l'indice dell'array	*/

		INCR(numstampa);

		}
	}
}
/* Fine di main() */

/* La routine di visualizzazione */

void
stampaev(ev)
struct InputEvent *ev;
{
register int tempmicro;
register int deltasecs=0;
register int deltamicro=0;

/* Calcoliamo l'intervallo di tempo trascorso e memorizziamo	*/
/* i valori dei secondi e dei microsecondi per il prossimo		*/
/* ciclo. Attenzione: alcuni InputEvent hanno "tv_secs" a 0.	*/

if (ev->ie_TimeStamp.tv_secs)
	{
	deltasecs = ev->ie_TimeStamp.tv_secs - oldsecs;

	if( (tempmicro = ev->ie_TimeStamp.tv_micro - oldmicro) >= 0 ) 
		deltamicro=tempmicro;
	else
		{
		deltasecs--;
		deltamicro = MILIONE + tempmicro;
		}

	oldsecs  = ev->ie_TimeStamp.tv_secs;
	oldmicro = ev->ie_TimeStamp.tv_micro;
	}

/* Visualizziamo i dati che ci interessano */

if(ev->ie_Class==IECLASS_RAWMOUSE) 		/* Movimento del mouse */
 
	printf("Cl %2x  Sc %1x  c %2x  q %4x  s %8x  m %5x  i %2x %5x  x %5d  y %5d\n",
		ev->ie_Class, ev->ie_SubClass, ev->ie_Code, ev->ie_Qualifier,
	    ev->ie_TimeStamp.tv_secs, ev->ie_TimeStamp.tv_micro, deltasecs, deltamicro,
	    ev->ie_X, ev->ie_Y );

else if(ev->ie_Class==IECLASS_RAWKEY) 	/* Pressione di un tasto */
 
	printf("Cl %2x  Sc %1x  c %2x  q %4x  s %8x  m %5x  i %2x %5x  u %2x %2x  pu %2x %2x\n",
		ev->ie_Class, ev->ie_SubClass, ev->ie_Code, ev->ie_Qualifier,
		ev->ie_TimeStamp.tv_secs, ev->ie_TimeStamp.tv_micro, deltasecs, deltamicro,
		(ev->ie_X>>8), (ev->ie_X & 0x00ff), (ev->ie_Y>>8), (ev->ie_Y & 0x00ff) );

else 									/* Tutti gli altri casi */

	printf("Cl %2x  Sc %1x  c %2x  q %4x  s %8x  m %5x  i %2x %5x  a %6x\n",
			ev->ie_Class, ev->ie_SubClass, ev->ie_Code, ev->ie_Qualifier,
	        ev->ie_TimeStamp.tv_secs,ev->ie_TimeStamp.tv_micro, deltasecs, deltamicro,
			ev->ie_EventAddress );

}
/* Fine di stampaev() */

/* La routine di chiusura */

void uscita(UBYTE codice)
{

/* Liberiamo il segnale allocato	*/
if(numerosignal!=-1) FreeSignal(numerosignal);

/* Eliminiamo il nostro handler e chiudiamo il device */
if(!openerr)
	{
	if(!ioerr)
		{
		inputIOSR->io_Command = IND_REMHANDLER;
		inputIOSR->io_Data = (APTR)&infohandler;
		DoIO(inputIOSR);
		}
	CloseDevice(inputIOSR);
	}

/* Liberiamo lo IOStdReq */
if(inputIOSR) DeleteStdIO(inputIOSR);

/* Chiudiamo il Port */
if(mioport) DeletePort(mioport);

/* Un messaggio */
printf("InputMonitor ha terminato. %s\n",msgerrore[codice]);

exit();
}
/* Fine di uscita() */
