/*************************************************
*       Name :	   lightpen
* This code installs a lightpendriver as an
* InputEvent-Handler.
* Left  Mousebutton on Pin 3
* Right Mousebutton on Pin 4
*
* For SAS/C compile with -v
* (no stack checking)
*
*	© Copyright by Andreas Klingler 1988,1991
*
* This program is freely distributable but is NOT public domain.
* It MAY NOT be included in any commercial package, or sold for
* ANY amount, without written permission from the author.
* However if may be included on any freely distributable package,
* including packages for which a nominal copying fee of no more than
* $5 is charged, as long as this copyright notice keeps intact.
* This software is distributed as-is, without any warranty.
*
*	      created 31.10.88 akk
*	      cleanup 27.01.91 akk
*************************************************/
#include <exec/types.h>
#include "intuition/intuition.h"
#include <exec/ports.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <devices/input.h>
#include <exec/devices.h>
#include <devices/inputevent.h>
#include "stdio.h"
#include "stdlib.h"
#include "graphics/gfxbase.h"
#include "graphics/view.h"
#include "proto/dos.h"
#include "proto/exec.h"
#include "proto/intuition.h"
#include "proto/graphics.h"
#include "hardware/custom.h"

/***************** CONSTANTS ********************/
#define LP_ENABLE 0x08
#define XCOMPENSATE 42 /* This is to compensate some timing problems.
			  You might need to change this. Uh! Again the
			  answer is 42! What was the question!? */

#define BANNER "\x9B\x30;33mLightpenhandler Version 1.1\x9B\x30m © by Andreas Klingler 1988,1991\n"
#define ACTIVATED " Handler installed\n"
#define TERMINATING " Handler removed\n"
#define PORTNAME "lightpen_AKK.port"

/****************** GLOBAL VARIABLES ************/
extern struct Custom __far custom;

struct MsgPort *inputDevPort;
struct IOStdReq *inputRequestBlock;
struct Interrupt handlerStuff;
struct OURMSG {
    struct Message msgpart;
    short dummy;
    } msg;

struct LpInfo { short *ViewX, *ViewY;  } handlerdata;

/* Prototypes for functions defined in lp.c */
struct InputEvent * __regargs __saveds myhandler(struct InputEvent *,
                                                 struct LpInfo *);
void main(void);
void chkabort(void);


/* Declarations for CBACK */
extern BPTR _Backstdout;         /* standard output when run in background */
long _BackGroundIO = 1;          /* Flag to tell it we want to do I/O      */
long _stack = 4000;              /* Amount of stack space our task needs   */
char *_procname = "Lightpenhandler";  /* The name of the task to create    */
long _priority = 20;             /* The priority to run us at              */

/******************* Here goes the Handler ****************/
struct InputEvent *
__regargs __saveds myhandler(ev, mydata)
	struct InputEvent *ev;  /* and a pointer to a list of events */
	struct LpInfo *mydata;  /* system will pass me a pointer to my 
				      own data space. */
		
{ 
	static struct InputEvent lpev; 
	struct InputEvent *retwert;
	BOOL lpen_invalid = FALSE,button_pressed = FALSE;
	static BOOL left_down,right_down;
	ULONG pos;
	static ULONG *lpen = (ULONG *)&custom.vposr;
	register short x,y;

	pos = *lpen;	/* read pen position (we read two
			   registers here: VPOSR,VHPOSR) */

	Forbid();
	  if ((pos != *lpen) || /* Test, if Lpen has been
				   triggered since VBLANK */
	     ((pos & 0x1ffff) > 0x12a00)) {  retwert=ev; lpen_invalid=TRUE; }
	   else retwert = &lpev;

	/* Translate in HIRES INLACE coordinates (depending of position of
	   the View). That`s what mouse coordinates are in.
	   Since we have only 8 bits for the horizonal position it gets
	   a little bit tricky (there are 640 pixel in one line in hires!) */
	  x = (short)((pos <<1) & 0x1ff);
	  if (x < 100) x += 454;
 	  y = ((short) (pos >> 8) &0x1ff) - *(mydata->ViewY);
 	  x -= (*(mydata->ViewX) + XCOMPENSATE);

	  lpev.ie_NextEvent = ev;  /* link lpen ahead of event stream */
	  lpev.ie_Class = IECLASS_POINTERPOS;
	  lpev.ie_Code = IECODE_NOBUTTON;
	  lpev.ie_Qualifier = 0;
	  lpev.ie_TimeStamp.tv_secs = 0;
	  lpev.ie_TimeStamp.tv_micro = 0;
	  lpev.ie_X = x << 1;
	  lpev.ie_Y = y << 1;

	/* Button on lightpen pressed ? */
	  if (right_down) {
	  	if (!(custom.joy1dat & 1)) { lpev.ie_Code = IECODE_RBUTTON |
							      IECODE_UP_PREFIX;
				  	     right_down = FALSE;
					     button_pressed = TRUE;
					   }
		        }
	  else if (custom.joy1dat & 1) { lpev.ie_Code = IECODE_RBUTTON;
				         right_down = TRUE;
					 button_pressed = TRUE;
					}
	  if (left_down) {
	  		if (!(custom.joy1dat & 0x100)){
					lpev.ie_Code = IECODE_LBUTTON |
						       IECODE_UP_PREFIX;
					left_down = FALSE;
					button_pressed = TRUE;
					}
		        }
		else    if (custom.joy1dat & 0x100) {
					lpev.ie_Code = IECODE_LBUTTON;
					left_down = TRUE;
					button_pressed = TRUE;
					}

	/* Append positiondata only if it is valid */
	  if (button_pressed && lpen_invalid) {
			lpev.ie_Class = IECLASS_RAWMOUSE;
			lpev.ie_Qualifier = IEQUALIFIER_RELATIVEMOUSE;
			lpev.ie_X = lpev.ie_Y = 0; 
			retwert = &lpev;
			}
	Permit();
	return (retwert);
}


void main()
{
	struct MsgPort *port;
	SHORT error;


	if (_Backstdout) Write(_Backstdout, BANNER, sizeof(BANNER));

	/* now see if we are allready installed */
      if (!(port = FindPort(PORTNAME)))  {
        /* if not, install ourself : */
	if ((port = CreatePort(PORTNAME,0)) == NULL) _exit (19);

	GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0);
	if (!GfxBase) _exit(20);
	
	IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0);
	if (!IntuitionBase) _exit(21);
			
	/* Tell the OS about the lightpen and enable the hardware */
	GfxBase->system_bplcon0 |= LP_ENABLE;
	RemakeDisplay();

	/* Determine position of the View. Attention: if someone pokes
	   around with Preferences while we are running, we can`t handle
	   this! */	       
	handlerdata.ViewX = (short *)&(GfxBase->ActiView->DxOffset);
	handlerdata.ViewY = (short *)&(GfxBase->ActiView->DyOffset);

	inputDevPort = CreatePort(0,0);		/* for input device */
	if(inputDevPort == NULL) _exit(1);	/* error during createport */
	inputRequestBlock=CreateStdIO(inputDevPort);
	if(inputRequestBlock == 0) { DeletePort(inputDevPort); _exit(2); }
					/* error during createstdio */


	handlerStuff.is_Data = (APTR)&handlerdata;
			/* address of its data area */
	handlerStuff.is_Code = (void *)myhandler;
			/* address of entry point to handler */
	handlerStuff.is_Node.ln_Pri = 51;
			/* set the priority one step higher than
		 	 * Intuition, so that our handler enters
			 * the chain ahead of Intuition.
			 */
	error = OpenDevice("input.device",0,(struct IORequest *)inputRequestBlock,0);
	if(error) _exit(23);

	inputRequestBlock->io_Command = IND_ADDHANDLER;
	inputRequestBlock->io_Data = (APTR)&handlerStuff;
		
	DoIO((struct IORequest *)inputRequestBlock);  /* Activate the Handler */
	
	/* The Handler is running. Close everything we don`t need
	   anymore and wait until it is time to uninstall the handler.
	   All the dirty work is done by the handler */

	CloseLibrary((struct Library *)IntuitionBase);
	CloseLibrary((struct Library *)GfxBase);

	if (_Backstdout) { Write(_Backstdout,ACTIVATED, sizeof(ACTIVATED));
			   Close(_Backstdout);
			   }
	WaitPort(port);  		/* Wait until we get message */
	GetMsg(port);			/* to exit, then remove it */
					/* and get out		   */

	inputRequestBlock->io_Command = IND_REMHANDLER;
	inputRequestBlock->io_Data = (APTR)&handlerStuff;
	DoIO((struct IORequest *)inputRequestBlock);

	/* close the input device */
	CloseDevice((struct IORequest *)inputRequestBlock);

	/* delete the IO request */
	DeleteStdIO(inputRequestBlock);

	/* free other system stuff */
	DeletePort(inputDevPort);
	DeletePort(port);
      }   /* this is the end of the BIG-IF (and of the program */

      else  /* in this case the handler is already installed, so we
	       send a message to it to make it deinstall itself */
	{
	msg.msgpart.mn_Length = sizeof(struct OURMSG);
	PutMsg(port,(struct Message *)&msg);
	if (_Backstdout) { Write(_Backstdout, TERMINATING, sizeof(TERMINATING));
			   Close(_Backstdout);
			   }
       }
}					/* end of main */

void MemCleanup() {}	/* We don`t want this out of lc.lib */

void chkabort() {}