/* November 6th, 1988 */

/* I took the MachMouse source code and cut it down to just the parts  */
/* which were needed for the pointer manipulation, So this source is   */
/* actually already in the PD. Anyway, I just thought I'd include it   */
/* because I HATE IT WHEN SOMEONE DOESN'T DISTRIBUTE THEIR SOURCE CODE */
/* WITH THEIR PROGRAM. drives me nuts. This is totally PD, ofcourse.   */
/* Probably not perfectly written, and no documentation - sorry. */
/* Used Manx C v3.4b */

/* Enjoy, K.Mardam-Bey */

#include <devices/input.h>
#include <devices/inputevent.h>
#include <devices/timer.h>
#include <exec/types.h>
#include <exec/interrupts.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <functions.h>
#include <graphics/gfxmacros.h>
#include <intuition/intuitionbase.h>

#define QUIT         0x1
#define MOVEIT       0x4
#define DELAY    1000000L
#define LAMIGA       0x66
#define STOPIT       0x41

char PortName[] = "PPT";

short left, right;
short dx,dy;
short x,y;
short event;
short lastcode = 0;
long  sec = 0,micro = 0;
short rbuttonisdown = 0, lbuttonisdown = 0;

struct MsgPort   *inputPort = NULL;
struct IOStdReq  *inputReq = NULL;
struct MsgPort   *TimerPort = NULL;
struct Screen    *s;
struct IntuitionBase *IntuitionBase = NULL;

struct timerequest Timer_Req;
long   TimerSig,tdevice = 1;

struct InputEvent phoney;
struct HotInfo
  {
  struct Task *hotTask;
  long  hotSig;
  } hotStuff;

long signum = -1;

struct Interrupt handlerStuff;
struct defPort
{
  struct MsgPort mp;
};

struct defPort *defPortPtr;
char defPortName[] = "MaDP";
short updating = 0;

void HandlerInterface()
{
#asm
  movem.l a4,-(sp)
  jsr _geta4#
  movem.l   A0/A1,-(sp)
  jsr       _myhandler
  addq.l    #8,A7
  movem.l (sp)+,a4
#endasm
}

struct InputEvent *myhandler(ev1, hotStuff)
struct InputEvent *ev1;
struct HotInfo *hotStuff;
{
  struct InputEvent *ev, *last;
  short removeit;
  short evcode,evqual;

  event = 0;
  for (ev=ev1,last = NULL; ev; ev=ev->ie_NextEvent)
  {
    evcode = ev->ie_Code;
    evqual = ev->ie_Qualifier;
    removeit = 0;

    if ((ev->ie_Class != IECLASS_TIMER))
    {
      if (ev->ie_Class == IECLASS_RAWKEY)
      {
        if ((evcode >= 0x80) && (evcode == (lastcode | 0x80))) {
          removeit = 1;
          lastcode = 0; }
        else
          if (evcode == LAMIGA) {
            lastcode = evcode;
            removeit = 1; }
        else {
          lastcode = 0;
          removeit = 0;
        }

        if ((evcode == STOPIT)&&((evqual & IEQUALIFIER_LCOMMAND) == IEQUALIFIER_LCOMMAND))
        {
          lastcode = evcode;
          removeit = 1;
          event |= QUIT;
        }
      }
    }

    if (ev->ie_Class == IECLASS_RAWMOUSE)
    {
      if (!left && (evcode == IECODE_LBUTTON))
        lbuttonisdown = 1;
      if (!right && (evcode == IECODE_RBUTTON))
        rbuttonisdown = 1;

      s = IntuitionBase->FirstScreen;
      while (s && (s->MouseY < 0)) s = s->NextScreen;
      if (s == NULL) s = IntuitionBase->ActiveScreen;

      x = s->MouseX + ev->ie_X;
      y = s->MouseY + ev->ie_Y;

      if ((ev->ie_TimeStamp.tv_secs == sec) && ((ev->ie_TimeStamp.tv_micro / 50000) == micro))
      {
        dx = ev->ie_X;
        dy = ev->ie_Y;
        if (!lbuttonisdown && !rbuttonisdown) event |= MOVEIT;
      }

      if (ev->ie_TimeStamp.tv_secs)
      {
        micro = ev->ie_TimeStamp.tv_micro / 50000;
        sec = ev->ie_TimeStamp.tv_secs;
      }

      if (evcode == (IECODE_LBUTTON | 0x80))
        lbuttonisdown = 0;
      if (evcode == (IECODE_RBUTTON | 0x80))
        rbuttonisdown = 0;
    }

    if (removeit)
      if (last == NULL)
        ev1 = ev->ie_NextEvent;
      else
        last->ie_NextEvent = ev->ie_NextEvent;
    else
      last = ev;
    }

  if (event)
    Signal(hotStuff->hotTask,hotStuff->hotSig);
  return(ev1);
}

main (argc, argv)
int argc;
char *argv[];
{
  struct IntuiMessage *Msg;
  long                class;
  long                len;
  short               i,j,f,c;

  IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library",0L);

  defPortPtr = (struct defPort *) FindPort(defPortName);
  if (defPortPtr == NULL)
  {
    if ((defPortPtr = (struct defPort *) AllocMem((long)sizeof(struct defPort),MEMF_PUBLIC | MEMF_CLEAR)) == NULL)
      exit(10);
  }
  else
    updating = 1;

  if (updating)
    Uninstall();

  i = left = right = 0;

  while (argv[1][i]) {
    if (argv[1][i] == 'l')
      left = 1;
    if (argv[1][i] == 'r')
      right = 1;
    i += 1;
  }

  defPortPtr->mp.mp_Node.ln_Pri = 0;
  defPortPtr->mp.mp_Node.ln_Type = NT_MSGPORT;
  NewList(&(defPortPtr->mp.mp_MsgList));
  defPortPtr->mp.mp_Node.ln_Name = (char *) &(defPortName);

  AddPort(defPortPtr);

  if((signum = AllocSignal((long)-1)) == -1)
    Uninstall();

  hotStuff.hotSig = 1 << signum;
  hotStuff.hotTask = FindTask(NULL);

  if(!(inputPort = CreatePort(PortName,0)))
    Uninstall();
  if(!(inputReq = CreateStdIO(inputPort)))
    Uninstall();

  handlerStuff.is_Data = (APTR)&hotStuff;
  handlerStuff.is_Code = HandlerInterface;
  handlerStuff.is_Node.ln_Pri = 55;
  handlerStuff.is_Node.ln_Name = "PopHan";

  if(OpenDevice("input.device",0L,inputReq,0L) != 0)
    Uninstall();

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

  DoIO(inputReq);

  if ((TimerPort = CreatePort("TimP", 0L)) == NULL)
    Uninstall();

  if ((tdevice = OpenDevice(TIMERNAME, UNIT_VBLANK, &Timer_Req, 0L)) != 0)
    Uninstall();
  Timer_Req.tr_node.io_Message.mn_ReplyPort = TimerPort;
  Timer_Req.tr_node.io_Command = TR_ADDREQUEST;
  Timer_Req.tr_node.io_Flags = 0;
  Timer_Req.tr_node.io_Error = 0;

  TimerSig = (1L << TimerPort->mp_SigBit);

  (void)SetTaskPri(FindTask(NULL), 0L);

  QueTimer();

  for (;;)
  {
    Wait(hotStuff.hotSig | TimerSig);

    if (Msg = (struct IntuiMessage *)GetMsg(TimerPort))
      if (Msg) QueTimer();

    if (event & MOVEIT)
      MovePointer();

    if (event & QUIT)
      Uninstall();
  }
}

QueTimer()
{
  Timer_Req.tr_time.tv_secs = 0;
  Timer_Req.tr_time.tv_micro = DELAY;
  SendIO(&Timer_Req.tr_node);
}

MovePointer()
{
  s = IntuitionBase->ActiveScreen;
  x = s->MouseX;
  y = s->MouseY;

  if (x < 1)
    dx = s->Width * 2;
  if (x >= s->Width - 1)
    dx = (s->Width * -2);
  if (y < 1)
    dy = s->Height * 3;
  if (y >= s->Height - 1)
    dy = (s->Height * -3);

  inputReq->io_Command = IND_WRITEEVENT;
  inputReq->io_Flags = 0;
  inputReq->io_Length = sizeof(struct InputEvent);
  inputReq->io_Data = (APTR)&phoney;

  phoney.ie_NextEvent = NULL;
  phoney.ie_Class = IECLASS_RAWMOUSE;
  phoney.ie_TimeStamp.tv_secs = 0;
  phoney.ie_TimeStamp.tv_micro = 0;
  phoney.ie_Code = IECODE_NOBUTTON;
  phoney.ie_Qualifier = IEQUALIFIER_RELATIVEMOUSE;
  phoney.ie_X = dx;
  phoney.ie_Y = dy;

  DoIO(inputReq);
}

Uninstall()
{
  if (!updating)
    {
    if (inputReq)
      {
      inputReq->io_Command = IND_REMHANDLER;
      inputReq->io_Data = (APTR)&handlerStuff;
      DoIO(inputReq);

      CloseDevice(inputReq);
      DeleteStdIO(inputReq);
      }

    if (inputPort)    DeletePort(inputPort);
    if (signum > -1)  FreeSignal(signum);

    if (tdevice == 0)
      {
      AbortIO(&Timer_Req.tr_node);
      CloseDevice(&Timer_Req);
      }

    if (TimerPort)
      DeletePort(TimerPort);

    if (defPortPtr)
      {
      if (defPortPtr->mp.mp_Node.ln_Name)
        RemPort(defPortPtr);
      FreeMem(defPortPtr,(long)sizeof(struct defPort));
      }
    }

  if (IntuitionBase)
    CloseLibrary(IntuitionBase);
  exit(0L);
}
