/*
    Modified to work with Manx 3.6a; probably won't work with Lattice.
                                Based on:
                                                                             */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* |_o_o|\\ Copyright (c) 1986 The Software Distillery.  All Rights Reserved */
/* |. o.| || This program may not be distributed without the permission of   */
/* | .  | || the authors.                                                    */
/* | o  | ||    Dave Baker     Ed Burnette  Stan Chow    Jay Denebeim        */
/* |  . |//     Gordon Keener  Jack Rouse   John Toebes  Doug Walker         */
/* ======          BBS:(919)-471-6436      VOICE:(919)-469-4210              */ 
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
 * VERY loosely based on the input.device example by Rob Peck, 12/1/85
 */
/* * * * * * * * * INCLUDE FILES * * * * * * * * * * * */
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <exec/interrupts.h>
#include <exec/ports.h>
#include <exec/libraries.h>
#include <exec/io.h>
#include <exec/tasks.h>
#include <exec/execbase.h>
#include <exec/devices.h>
#include <devices/timer.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <devices/console.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <graphics/gfxmacros.h>
#include <hardware/custom.h>
#include <hardware/dmabits.h>
/*
 *   I need this kludge because of a bug in functions.h.
 */
#define ConsoleDevice IDontReallyExist
#include <functions.h>
#undef ConsoleDevice
#include <stdio.h>

/* * * * * * * * * * * CONSTANTS * * * * * * * * * * * * */
#define PORTNAME     "Mackie.port"
#define TIMEINTERVAL 1L      /* in seconds */
#define DEFTIME      300     /* two minute timeout */
#define MAXCMD       200
#define MAXPENDINGSYSTEM 20
#define DEFKEY    0x45
#define DEFCMD    "NEWCLI >NIL: <NIL:"
#define STARTUPFILE "s:.mackierc"
/*
 *   Macro to make BPTR things easier to work with.
 */
#define BSTRtoS(a) ((char *)(((long)(a))<<2))
/* * * * * * * * * * * GLOBAL VARIABLES * * * * * * * * * */
/*
 *   Detach stuff (doesn't work, somehow!)
 */
typedef struct
   {
   struct Task          *buddy ;
   ULONG                 creatclisig ;
   ULONG                 unblanksig ;
   ULONG                 frontsig ;
   ULONG                 noevents ;
   short                 creatsignum ;
   short                 blanksignum ;
   short                 replysignum ;
   short                 frontsignum ;
   short                 key ;
   char                  frontkey[MAXPENDINGSYSTEM] ;
   char                  frontqual[MAXPENDINGSYSTEM] ;
   short                 frontptr ;
   short                 draw ;
   short                 helppressed ;
   struct Screen        *blankscreen ;
   } GLOBAL_DATA;
#define SHIFT 1
#define NOTCLI 2
#define NOTINTUITION 4
struct hotkey {
   struct hotkey*next ;
   char key, flags ;
   int structlen ;
   char *matchstring, *startstring ;
   char strings[2] ;
} *hotkeys ;
struct Window *lastwindows[200] ;
struct Task *task ;
long taskreply ; /* the signal the line drawing task will return */
#define STACKSIZE (1000)
long stackmem[STACKSIZE/4] ;
struct InfoData *infoptr ;
struct MsgPort *FindPort(), *CreatePort();
void DeletePort();
char *startupfile = STARTUPFILE ;
struct OURMSG {
 struct Message msgpart;
 short key;
 short interval;
 short draw ;
 char cmd[MAXCMD];
 };

extern char *strcpy() ;

/************************************************************************/
/* the handler subroutine - called through the handler stub             */
/************************************************************************/
extern void HandlerInterface();
void foobar() {
#asm
_HandlerInterface:
	movem.l a4,-(a7)
	movem.L	A0/A1,-(A7)
	jsr	_geta4#
	jsr	_myhandler
	addq.L	#8,A7
	movem.L	(a7)+,a4
	rts
#endasm
}
char keytoasc[128] ;
struct InputEvent *myhandler(ev, gptr)
struct InputEvent *ev;      /* and a pointer to a list of events */
register GLOBAL_DATA *gptr;      /* Everything we need to know about */
{
   register struct InputEvent *ep, *laste;
   int key ;
   /*
    * run down the list of events
    * to see if they pressed
    * one of the magic buttons
    */
   for (ep = ev, laste = NULL; ep != NULL; ep = ep->ie_NextEvent) {
      if ((ep->ie_Class == IECLASS_RAWKEY)    &&
             (((ep->ie_Qualifier & IEQUALIFIER_LCOMMAND) &&
               (ep->ie_Code == gptr->key)) ||
              (!gptr->helppressed && ep->ie_Code == 0x5f) ||
              (gptr->helppressed &&
               (ep->ie_Code & 0x80) == 0 &&
/*
 *   All the qualifiers have code = 0x6?; dangerous to take advantage
 *   of?
 */
               (ep->ie_Code & 0xf0) != 0x60 &&
               gptr->frontkey[gptr->frontptr] == 0 &&
               (gptr->helppressed = (keytoasc[ep->ie_Code] != '.'))))) {
         if ((ep->ie_Qualifier & IEQUALIFIER_LCOMMAND) &&
              (ep->ie_Code == gptr->key))
            key = -1 ;
         else if (gptr->helppressed) {
            gptr->frontkey[gptr->frontptr] = key = ep->ie_Code ;
            gptr->frontqual[gptr->frontptr] = ep->ie_Qualifier ;
            gptr->frontptr++ ;
            if (gptr->frontptr >= MAXPENDINGSYSTEM)
               gptr->frontptr = 0 ;
            gptr->helppressed = 0 ;
         } else {
            gptr->helppressed = 1 ;
            key = 0 ;
         }
         /* we can handle this event so take it off the chain */
         if (laste == NULL)
            ev = ep->ie_NextEvent;
         else
            laste->ie_NextEvent = ep->ie_NextEvent;
         /* now tell him to create the new cli */
         if (key == -1)
            Signal(gptr->buddy, gptr->creatclisig);
         else if (key > 0)
            Signal(gptr->buddy, gptr->frontsig);
      } else
         laste = ep;
      if (ep->ie_Class != IECLASS_TIMER) {
         gptr->noevents = 0;
         if (gptr->blankscreen != NULL)
            Signal(gptr->buddy, gptr->unblanksig);
      }
   }
   /* pass on the pointer to the event */
   return(ev);
}

/* * * * * * * * * * * EXTERNAL ROUTINES * * * * * * * * * */
struct IntuitionBase *IntuitionBase;
struct GfxBase       *GfxBase;
struct DosLibrary    *DosBase;
struct NewScreen      NewScreen = 
   { 0, 0, 320, 30, 1, 0, 1, NULL, CUSTOMSCREEN, NULL, NULL, NULL, NULL };

extern struct MsgPort  *CreatePort();
struct IOStdReq *CreateIOReq();
void DeleteIOReq();

/************************************************************************/
/* Queue a timer to go off in a given number of seconds                 */
/************************************************************************/
void QueueTimer(tr,seconds)
struct timerequest *tr;
ULONG seconds;
{
   tr->tr_node.io_Command = TR_ADDREQUEST;   /* add a new timer request */
   tr->tr_time.tv_secs =  seconds;        	/* seconds */
   tr->tr_time.tv_micro = 0;
   SendIO( (struct IORequest *)tr );
}
/************************************************************************/
/* the main program to do the popcli stuff                              */
/************************************************************************/
GLOBAL_DATA global;
main(argc, argv)
int argc ;
char *argv[] ;
{
   struct MsgPort *port;
   int stay = 0;
   struct OURMSG *msg;
   int i ;
   char cmdstr[MAXCMD];
   short key, timeout;
   struct FileHandle *nullfh = NULL ;
   ULONG sig, timersig;
   struct timerequest *timerreq;
   struct MsgPort     *timerport;
   struct MsgPort     *inputDevPort;
   struct IOStdReq    *inputRequestBlock;
   struct Interrupt      handlerStuff;
   char *cmd ;
   int draw = 0 ;
   int nextfront = 0 ;

   SetTaskPri(FindTask(0L), 20L) ;
   global.creatsignum  = -1;
   global.blanksignum  = -1;
   global.replysignum  = -1;
   global.frontsignum  = -1;
   global.blankscreen  = NULL;
   global.key          = DEFKEY ;
   global.draw         = 1 ;
   timerreq            = NULL;
   timerport           = NULL;
   inputDevPort        = NULL;
   inputRequestBlock   = NULL;
   /* now see if we are already installed */
   if ((port = FindPort(PORTNAME)) == NULL) {
      stay = 1; /* remember to hang around when we are done */
      /* not installed, we need to install our own port */
      if ((port = CreatePort(PORTNAME,0L)) == NULL)
         goto abort;
   }
/*
 *   If we are hanging around, initialize our keyboard translation table.
 *   If we have difficulty, exit angry.
 */
   if (stay)
      if (initkeytoasc())
         goto abort ;
   /* now send the parameter to the waiting program */
   if ((msg = (struct OURMSG *)
       AllocMem((long)sizeof(struct OURMSG), MEMF_CLEAR|MEMF_PUBLIC)) == NULL)
      goto abort;
   if ((infoptr = (struct InfoData *)
      AllocMem((long)sizeof(struct InfoData), MEMF_CLEAR)) == NULL)
      goto abort ;
   /* fill in the message information */
   msg->msgpart.mn_Length = sizeof(struct OURMSG);
   strcpy(cmdstr, DEFCMD);
   timeout = 0 ;
   key = 0 ;
   msg->cmd[0] = 0;
   /* if we were run from CLI then output our banner and process parameters */
   if (argc > 0) {
      /* display our copyright */
      if (stay)
         puts("Mackie 1.1 by Tomas Rokicki - Copyright \xa9 1987, 1988 Radical Eye Software") ;
      if (argc == 1)
         puts("Usage: Mackie [-q] [-l] [-b] [-f startup] [time] [\"command\"]") ;
      argc-- ;
      argv++ ;
      while (argc > 0) {
         cmd = argv[0] ;
         if (*cmd == '-') {
            cmd++ ;
            switch (*cmd) {
case 'q' : case 'Q' :
               key = -1 ;
               puts("\x9B1mMackie\x9B0m Terminating") ;
               break ;
case 'l' : case 'L' :
               draw = 1 ;
               break ;
case 'b' : case 'B' :
               draw = -1 ;
               break ;
case 'f' : case 'F' :
               if (cmd[1])
                  startupfile = cmd + 1 ;
               else {
                  argv++ ;
                  argc-- ;
                  startupfile = argv[0] ;
               }
default :
               puts("Error in parameter!") ;
            }
         } else if ('0' <= *cmd && *cmd <= '9') {
            timeout = 0;
            while ((*cmd >= '0') && (*cmd <= '9'))
               timeout = (timeout*10) + *cmd++ - '0';
            if (timeout <= 0)
               timeout = DEFTIME;
         } else {
            strcpy(msg->cmd, cmd) ;
         }
         argc-- ;
         argv++ ;
      }
   }
   msg->interval = timeout;
   msg->key = key;
   msg->draw = draw ;
   if (stay)
      processstartup(startupfile, msg) ;
   if (draw)
      global.draw = draw ;
   PutMsg(port,(struct Message *)msg);
   if (!stay) goto abort;
   if (timeout == 0)
      timeout = DEFTIME ;
   global.blankscreen = NULL;
   global.buddy = FindTask(0L);
   global.noevents = 0;
   nullfh = Open("NIL:", MODE_NEWFILE);
   if (((inputDevPort = CreatePort(0L,0L)) == NULL) ||
      ((inputRequestBlock =
          CreateIOReq(inputDevPort, (long)sizeof(struct IOStdReq))) == NULL) ||
      ((timerport = CreatePort(0L,0L)) == NULL) ||
      ((timerreq  = (struct timerequest *)
          CreateIOReq(timerport, (long)sizeof(struct timerequest))) == NULL) ||
      ((global.creatsignum = AllocSignal(-1L)) == -1) ||
      ((global.blanksignum = AllocSignal(-1L)) == -1) ||
      ((global.replysignum = AllocSignal(-1L)) == -1) ||
      ((global.frontsignum = AllocSignal(-1L)) == -1) ||
      ((GfxBase = (struct GfxBase *)
                  OpenLibrary("graphics.library", 0L)) == NULL) ||
      ((IntuitionBase = (struct IntuitionBase *)
                        OpenLibrary("intuition.library", 0L)) == NULL) ||
      OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *)timerreq, 0L) ||
      OpenDevice("input.device",0L,(struct IORequest *)inputRequestBlock,0L))
         goto abort;
   handlerStuff.is_Data = (APTR)&global;
   handlerStuff.is_Code = HandlerInterface;
   handlerStuff.is_Node.ln_Pri = 51;
   timersig            = (1L << timerport->mp_SigBit);
   global.creatclisig  = 1L << global.creatsignum;
   global.unblanksig   = 1L << global.blanksignum;
   global.frontsig     = 1L << global.frontsignum;
   inputRequestBlock->io_Command = IND_ADDHANDLER;
   inputRequestBlock->io_Data    = (APTR)&handlerStuff;
   DoIO((struct IORequest *)inputRequestBlock);
   QueueTimer(timerreq, TIMEINTERVAL);
   for(;;) {         /* FOREVER */
      sig = Wait( global.creatclisig | global.unblanksig | timersig |
                  global.frontsig);
      /* see if they asked us to change the interval */
      if ((msg = (struct OURMSG *)GetMsg(port)) != NULL) {
         if (msg->cmd[0]) strcpy(cmdstr, msg->cmd);
         if (msg->key)
            global.key = msg->key;
         if (msg->interval)
            timeout    = msg->interval;
         if (msg->draw)
            global.draw = msg->draw ;
         FreeMem((char *)msg, (long)msg->msgpart.mn_Length);
         if (msg->key == -1) goto abort;
      }
      if ((sig & global.unblanksig) && global.blankscreen)
         screenunblank() ;
      if (sig & global.creatclisig) {
         WBenchToFront();
         (void)Execute(cmdstr,nullfh,nullfh);
      }
      if (sig & global.frontsig) {
         while (i=global.frontkey[nextfront]) {
            windowtofront(keytoasc[i], global.frontqual[nextfront]) ;
            global.frontkey[nextfront] = 0 ;
            nextfront++ ;
            if (nextfront >= MAXPENDINGSYSTEM)
               nextfront = 0 ;
         }
      }
      if (sig & timersig) {
         /* get rid of the message */
         (void)GetMsg(timerport);
         QueueTimer(timerreq, TIMEINTERVAL);
         if (task)
            SetTaskPri(task, 10L) ;
         if ((global.noevents++ >= timeout) && (global.blankscreen == NULL))
            blankscreen() ;
      }
   }
abort:
   if (infoptr) {
      FreeMem(infoptr, (long)sizeof(struct InfoData)) ;
      infoptr = NULL ;
   }
   if (timerreq != NULL) {
      if (timerreq->tr_node.io_Device != NULL)
         CloseDevice((struct IORequest *)timerreq);
      DeleteIOReq((struct IOStdReq *)timerreq);
   }
   if (inputRequestBlock != NULL) {
      if (inputRequestBlock->io_Device != NULL) {
         inputRequestBlock->io_Command = IND_REMHANDLER;
         inputRequestBlock->io_Data = (APTR)&handlerStuff;
         DoIO((struct IORequest *)inputRequestBlock);
         CloseDevice((struct IORequest *)inputRequestBlock);
      }
      DeleteIOReq(inputRequestBlock);
   }
   screenunblank() ;
   if (timerport != NULL)          DeletePort(timerport);
   if (global.creatsignum != -1)   FreeSignal(global.creatsignum);
   if (global.blanksignum != -1)   FreeSignal(global.blanksignum);
   if (global.replysignum != -1)   FreeSignal(global.replysignum);
   if (global.frontsignum != -1)   FreeSignal(global.frontsignum);
   if (IntuitionBase != NULL)      CloseLibrary((struct Library *)IntuitionBase);
   if (GfxBase != NULL)            CloseLibrary((struct Library *)GfxBase);
   if (inputDevPort != NULL)       DeletePort(inputDevPort);
   if (stay && (port != NULL))     DeletePort(port);
   {
      struct hotkey *hk ;

      while (hotkeys) {
         hk = hotkeys->next ;
         FreeMem(hotkeys, (long)hotkeys->structlen) ;
         hotkeys = hk ;
      }
   }
   if (nullfh)                     Close(nullfh);
   SetTaskPri(FindTask(0L), 0L) ;
}

struct IOStdReq *
CreateIOReq(port, size)
struct MsgPort *port;
long size;
{
   struct IOStdReq *ioReq;

   if ((ioReq = (struct IOStdReq *)
                AllocMem(size, MEMF_CLEAR | MEMF_PUBLIC)) != NULL) {
      ioReq->io_Message.mn_Node.ln_Type = NT_MESSAGE;
      ioReq->io_Message.mn_Node.ln_Pri  = 0;
      ioReq->io_Message.mn_Length       = size;
      ioReq->io_Message.mn_ReplyPort    = port;
   }
   return(ioReq);
}

void DeleteIOReq(ioReq)
struct IOStdReq *ioReq;
{
   ioReq->io_Message.mn_Node.ln_Type = 0xff;
   ioReq->io_Device = (struct Device *) -1;
   ioReq->io_Unit = (struct Unit *) -1;
   FreeMem( (char *)ioReq, (long)ioReq->io_Message.mn_Length);
}
/*
 *   All of this stuff down here was written by Tomas Rokicki.
 *   (C) Copyright 1987, 1988, Radical Eye Software.
 */
#include "graphics/gfxbase.h"
/*
 *   The maximum number of lines on the screen at once.
 */
#define MAXLINES (100)
/*
 *   The external variables we access.
 */
struct RastPort *rastport ;
short screenheight, screenwidth ;
/*
 *   Some locals to this file.
 */
static struct NewScreen newscreen = {
   0, 0,
   640, 400,
   1,
   0, 1,
   HIRES | LACE | SCREENQUIET,
   CUSTOMSCREEN,
   NULL,
   NULL,
   NULL,
   NULL } ;
/*
 *   This routine opens a screen and fires off the task if apropriate.
 */
void taskrout() ;
blankscreen() {
   screenheight = 2 * GfxBase->NormalDisplayRows ;
   screenwidth = GfxBase->NormalDisplayColumns ;
   newscreen.Height = screenheight ;
   newscreen.Width = screenwidth ;
   if (global.draw == -1 || AvailMem(MEMF_CHIP) < 70000L ||
             (global.blankscreen = OpenScreen(&newscreen)) == NULL) {
      if ((global.blankscreen = OpenScreen(&NewScreen)) != NULL) {
         SetRGB4(&(global.blankscreen->ViewPort), 0L, 0L, 0L, 0L);
         OFF_DISPLAY ;
      }
   } else {
      if (global.blankscreen == NULL &&
          (global.blankscreen = OpenScreen(&newscreen))==NULL)
         return ;
/*
 *   Turning off the sprites is a little bit tricky.  A simple OFF_SPRITE
 *   will continue to display the data in the current sprite registers.
 *   This happens most often with the cursor.  To fix, we simply clear out
 *   the sprite control registers after turning the sprites off.  This
 *   might break all of the sprites when the system comes back up . . .
 */
      OFF_SPRITE ;
      custom.spr[0].ctl = 0 ;
      custom.spr[1].ctl = 0 ;
      custom.spr[2].ctl = 0 ;
      custom.spr[3].ctl = 0 ;
      custom.spr[4].ctl = 0 ;
      custom.spr[5].ctl = 0 ;
      custom.spr[6].ctl = 0 ;
      custom.spr[7].ctl = 0 ;
      SetRGB4(&(global.blankscreen->ViewPort), 0L, 0L, 0L, 0L) ;
      rastport = &(global.blankscreen->RastPort) ;
      SetAPen(rastport, 0L) ;
      Forbid() ;
      RectFill(rastport, 0L, 0L, (long)screenwidth-1, 30L) ;
      Permit() ;
      SetAPen(rastport, 1L) ;
      task = (struct Task *)AllocMem((long)sizeof(struct Task),
                MEMF_CLEAR | MEMF_PUBLIC) ;
      if (task != NULL) {
         task->tc_Node.ln_Pri = 10 ;
         task->tc_Node.ln_Type = NT_TASK ;
         task->tc_Node.ln_Name = "ri.Lines" ;
         task->tc_SPLower = (APTR)stackmem ;
         task->tc_SPUpper = task->tc_SPReg = (APTR)(stackmem + STACKSIZE/4 - 8) ;
         AddTask(task, taskrout, 0L) ;
      }
   }
}
/*
 *   Unblank the screen.  We kill the task with the standard ^C kill signal.
 */
screenunblank() {
   if (task != NULL) {
      Signal(task, 1L << SIGBREAKB_CTRL_C) ;
      SetTaskPri(task, 11L) ;
      Wait(1L << global.replysignum) ;
      RemTask(task);
      FreeMem(task, (long)sizeof(struct Task)) ;
      task = NULL ;
   }
   if (global.blankscreen != NULL) {
      CloseScreen(global.blankscreen);
      global.blankscreen = NULL ;
      ON_DISPLAY ;
      ON_SPRITE ;
   }
}
/*
 *   This routine returns a random value from 0 to n-1.
 */
int randm(i)
int i ;
{
   static long seed ;
   long rval ;

   if (seed == 0)
      seed = 323214521 + global.blankscreen->MouseX +
              global.blankscreen->MouseY ;
   seed = seed * 123213 + 121 ;
   rval = (seed >> 5) & 65535 ;
   return ((i * rval) >> 16) ;
}
/*
 *   This routine sets x and y values to a random number.
 */
static long x, y ;
randomxy() {
   x = randm(screenwidth) ;
   y = randm(screenheight) ;
}
/*
 *   Main routines are always fun.
 */
short x1store[MAXLINES], y1store[MAXLINES] ;
short x2store[MAXLINES], y2store[MAXLINES] ;
short ptr ;
short dx1, dy1, dx2, dy2 ;
short ox1, oy1, ox2, oy2 ;
short nx1, ny1, nx2, ny2 ;
short dr, dg, db ;
short or, og, ob ;
short nr, ng, nb ;
/*
 *   Initialize things for the first lines.
 */
startlines() {
   ptr = 0 ;
   if (dx1 == 0) {
      ox1 = randm(screenwidth) ;
      ox2 = randm(screenwidth) ;
      oy1 = randm(screenheight) ;
      oy2 = randm(screenheight) ;
      dx1 = 3 ;
      dx2 = 4 ;
      dy1 = 1 ;
      dy2 = 6 ;
      nr = 53 ;
      ng = 33 ;
      nb = 35 ;
      dr = -3 ;
      dg = 5 ;
      db = 7 ;
   }
   SetRGB4(&(global.blankscreen->ViewPort), 0L, 0L, 0L, 0L) ;
   SetRGB4(&(global.blankscreen->ViewPort), 1L, (long)(nr >> 3),
                                 (long)(ng >> 3), (long)(nb >> 3)) ;
}
/*
 *   Advance the number by the delta, and check the boundaries.
 */
adv(o, d, n, w)
short *o, *d, *n ;
short w ;
{
   *n = *o + *d ;
   if (*n < 0) {
      *n = 0 ;
      *d = randm(6) + 1 ;
   } else if (*n >= w) {
      *n = w - 1 ;
      *d = - randm(6) - 1 ;
   }
}
/*
 *   Advance the two points which make up the lines.
 */
advancelines() {
   adv(&ox1, &dx1, &nx1, screenwidth) ;
   adv(&ox2, &dx2, &nx2, screenwidth) ;
   adv(&oy1, &dy1, &ny1, screenheight) ;
   adv(&oy2, &dy2, &ny2, screenheight) ;
}
/*
 *   Draw a new set of lines.
 */
drawnew() {
   x1store[ptr] = ox1 = nx1 ;
   x2store[ptr] = ox2 = nx2 ;
   y1store[ptr] = oy1 = ny1 ;
   y2store[ptr] = oy2 = ny2 ;
   Move(rastport, (long)ox1, (long)oy1) ;
   Draw(rastport, (long)ox2, (long)oy2) ;
   Draw(rastport, (long)(screenwidth-ox1-1), (long)(screenheight-oy1-1)) ;
   Draw(rastport, (long)(screenwidth-ox2-1), (long)(screenheight-oy2-1)) ;
   Draw(rastport, (long)ox1, (long)oy1) ;
   ptr++ ;
   if (ptr == MAXLINES)
      ptr = 0 ;
}
/*
 *   Erase the old line.
 */
eraseold() {
   short oldpen ;

   oldpen = rastport->FgPen ;
   SetAPen(rastport, 0L) ;
   Move(rastport, (long)x1store[ptr], (long)y1store[ptr]) ;
   Draw(rastport, (long)x2store[ptr], (long)y2store[ptr]) ;
   Draw(rastport, (long)(screenwidth-x1store[ptr]-1),
                  (long)(screenheight-y1store[ptr]-1)) ;
   Draw(rastport, (long)(screenwidth-x2store[ptr]-1), 
                  (long)(screenheight-y2store[ptr]-1)) ;
   Draw(rastport, (long)x1store[ptr], (long)y1store[ptr]) ;
   SetAPen(rastport, (long)oldpen) ;
}
/*
 *   This routine mucks with the colors.
 */
colors() {
   or = nr ;
   og = ng ;
   ob = nb ;
   adv(&or, &dr, &nr, 128) ;
   adv(&og, &dg, &ng, 128) ;
   adv(&ob, &db, &nb, 128) ;
   SetRGB4(&(global.blankscreen->ViewPort), 1L, (long)(nr >> 3),
                                    (long)(ng >> 3), (long)(nb >> 3)) ;
}
/*
 *   Our actual task, in an infinite loop.
 */
void taskrout() {
   long i ;
   struct Task *task ;

   geta4() ;
   task = FindTask(0L) ;
   startlines() ;
   for (i=0; i<MAXLINES; i++) {
      advancelines() ;
      drawnew() ;
      if (SetSignal(0L, 0L))
         goto done ;
   }
   colors() ;
   while (SetSignal(0L, 0L)==0) {
      if (task->tc_Node.ln_Pri == 10)
         SetTaskPri(task, -20L) ;
      eraseold() ;
      advancelines() ;
      drawnew() ;
      eraseold() ;
      advancelines() ;
      drawnew() ;
      if (task->tc_Node.ln_Pri == 10)
         SetTaskPri(task, -20L) ;
      eraseold() ;
      advancelines() ;
      drawnew() ;
      eraseold() ;
      advancelines() ;
      drawnew() ;
      if (task->tc_Node.ln_Pri == 10)
         SetTaskPri(task, -20L) ;
      eraseold() ;
      advancelines() ;
      drawnew() ;
      eraseold() ;
      advancelines() ;
      drawnew() ;
      colors() ;
   }
done:
   Signal(global.buddy, 1L << global.replysignum) ;
   Wait(0L) ;
}
/*
 *   Now we do hotkey magic to activate windows, bring them to front,
 *   etc.
 *
 *   Now we have a key, so we have to find a process with that name and
 *   bring her to front.  For now, we just deal with tasks, since the
 *   CLI stuff is so complicated.
 */
#define MAXMATCH (20)
char simplematch[3] = { ' ', '*', 0 } ;
struct Window *matchwindows[MAXMATCH] ;
extern long LockIBase() ;
windowtofront(key, qual)
char key ;
{
   long foo ;
   int i, j ;
   int n ;
   struct Window *w ;
   struct Screen *s ;
   struct Process *p ;
   struct MsgPort **mp ;
   struct CommandLineInterface *CLI ;
   int cli ;
   char *nameptr, *matchptr ;
   extern struct DosLibrary *DOSBase ;
   struct hotkey *hk ;
   int shift, ctrl ;
   struct Window *activewindow ;

/*
 *   First we look for a matching record.
 */
   if (key == '.')
      goto goner ;
   shift = ((qual & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) != 0) ;
   ctrl = ((qual & IEQUALIFIER_CONTROL) != 0) ;
   activewindow = NULL ;
   for (hk=hotkeys; hk; hk=hk->next)
      if (hk->key == key && 
          (hk->flags & SHIFT) == shift)
         break ;
   cli = 0 ;
   if (hk && hk->matchstring)
      matchptr = hk->matchstring ;
   else {
      matchptr = simplematch ;
      if (key == ' ')
         matchptr++ ;
      else if ('A' <= key && key <= 'Z')
         simplematch[0] = key ;
      else if ('0' <= key && key <= '9') {
         cli = key ;
         if (cli == '0')
            cli += 10 ;
         matchptr++ ;
      } else
         goto goner ;
   }
   n = 0 ;
   foo = LockIBase(0L) ;
   if (! ctrl && !(hk && (hk->flags & NOTINTUITION)) && !cli) {
      for (s=IntuitionBase->FirstScreen; s; s=s->NextScreen)
         for (w=s->FirstWindow; w; w=w->NextWindow) {
            if (w->UserPort) {
               p = (struct Process *)(w->UserPort->mp_SigTask) ;
               if (((struct Task *)p)->tc_Node.ln_Type == NT_PROCESS) {
                  if (p->pr_CLI) {
                     CLI = (struct CommandLineInterface *)BSTRtoS(p->pr_CLI) ;
                     if (bstrcmp(BSTRtoS((CLI)->cli_CommandName), matchptr) &&
                         n < MAXMATCH) {
                           if (w==IntuitionBase->ActiveWindow)
                              activewindow = w ;
                           matchwindows[n++] = w ;
                        }
                  } else {
                     if (sstrcmp(((struct Task *)p)->tc_Node.ln_Name, matchptr)
                          && n < MAXMATCH) {
                        if (w==IntuitionBase->ActiveWindow)
                           activewindow = w ;
                        matchwindows[n++] = w ;
                     }
                  }
               }
            }
         }
   }
/*
 *   Folks, there are still several/many windows of vulnerability
 *   here; I'll have to plug them.  For instance, what happens if a
 *   CLI goes away while we are doing this?  Or what happens if a
 *   window goes away later, when we are looking at IntuitionBase?
 */
   if (! ctrl && !(hk && (hk->flags & NOTCLI))) {
      mp = (struct MsgPort **)BSTRtoS(*(DOSBase->dl_Root)) ;
      for (j=1; j<(long)(mp[0]); j++)
         if (mp[j] && (cli == 0 || j == cli - '0')) {
            p = (struct Process *)(mp[j]->mp_SigTask) ;
            CLI = (struct CommandLineInterface *)BSTRtoS(p->pr_CLI) ;
            if (!CLI->cli_Background &&
                  bstrcmp(BSTRtoS((CLI)->cli_CommandName), matchptr)) {
               infoptr->id_VolumeNode = NULL ;
               dos_packet(p->pr_ConsoleTask, ACTION_DISK_INFO,
                         ((long)(infoptr)) >> 2, 0L, 0L, 0L, 0L, 0L, 0L) ;
               if (w=(struct Window *)infoptr->id_VolumeNode)
                  if (n < MAXMATCH) {
                     if (w==IntuitionBase->ActiveWindow)
                        activewindow = w ;
                     matchwindows[n++] = w ;
                  }
            }
         }
   }
   if (shift)
      key += 100 ;
   if (ctrl || n==0) {
      UnlockIBase(foo) ;
      if (hk == NULL || hk->startstring == NULL)
         goto goner ;
      Execute(hk->startstring, 0L, 0L) ;
   } else {
      if (n == 1) {
         w = matchwindows[0] ;
      } else {
/*
 *   This is some real neat code.  We want to find the next window,
 *   that is, the window with the least address greater than the
 *   currently active window, unless the currently active window
 *   has the highest address, in which case we want to find the
 *   window with the least address.  Why this works is left as a
 *   puzzle for the reader.
 */
         if (activewindow) {
            w = activewindow ;
            for (j=0; j<n; j++)
               if ((w > activewindow) ^
                   (w > matchwindows[j]) ^
                   (matchwindows[j] > activewindow))
                  w = matchwindows[j] ;
         } else {
            w = NULL ;
            for (j=0; j<n; j++)
               if (lastwindows[key]==matchwindows[j]) {
                  w = lastwindows[key] ;
                  break ;
               }
            if (w==NULL)
               w = matchwindows[0] ;
         }
      }
      lastwindows[key] = w ;
      s = w->WScreen ;
      UnlockIBase(foo) ;
      ScreenToFront(s) ;
/*
 *   If only one window on screen, don't bring it to front
 *   (mostly for DPaint, but for other progs as well.)
 *   (Anyone know an easy way to see if this window is fully
 *   exposed?)
 */
      if (s->FirstWindow != w || w->NextWindow)
         WindowToFront(w) ;
      ActivateWindow(w) ;
   }
   return ;
goner:
   DisplayBeep(0L) ;
}
/*
 *   These two functions compare a given string `s' with a `key' string.
 *   The key string should be all upper case; this is a case insensitive
 *   match.  If the key string contains `*', this character is assumed to
 *   match the rest of the string (and it can only come at the end.)  We
 *   have a routine for BSTR's, and a routine for regular strings.
 */
int sstrcmp(s, key)
register char *s, *key ;
{
   while (1) {
      if (*key == '*')
         return(1) ;
      if (*key == 0)
         return(*s == 0) ;
      if (*s == 0)
         return(0) ;
      if (*s != *key &&
          (*s != *key + 32 || *s < 'a' || *s > 'z'))
         return(0) ;
      s++ ;
      key++ ;
   }
}
/*
 *   This is the same as above, only instead of using a null to
 *   end the string, we keep track of the number of characters.
 */
int bstrcmp(s, key)
char *s, *key ;
{
   int n ;

   n = *(unsigned char *)s++ ;
   while (1) {
      if (*key == '*')
         return(1) ;
      if (*key == 0)
         return(n == 0) ;
      if (n == 0)
         return(0) ;
      if (*s != *key &&
          (*s != *key + 32 || *s < 'a' || *s > 'z'))
         return(0) ;
      s++ ;
      key++ ;
      n-- ;
   }
}
/*
 *   A place to hold an input line.
 */
#define MAXSTARTUPLINE (100)
char startbuf[MAXSTARTUPLINE] ;
char upline[MAXSTARTUPLINE] ;
/*
 *   Is a legit separator of some sort.
 */
int issep(s)
register char s ;
{
   return (s <= ' ' || s == '=' || s == ':' || s == ',' || s == '-') ;
}
/*
 *   Go to next `word' in the startup file.
 */
char *getword(s)
register char *s ;
{
   while (*s && issep(*s))
      s++ ;
   return(s) ;
}
/*
 *   Upper case a string.
 */
char *upcase(dest, s)
char *dest ;
register char *s ;
{
   register char *d = dest ;

   while (*s) {
      if ('a' <= *s && *s <= 'z')
         *d++ = *s++ - 32 ;
      else
         *d++ = *s++ ;
   }
   *d = 0 ;
   return(dest) ;
}
/*
 *   Say we got a bad line.
 */
badline() {
   puts("Error in startup file!") ;
   puts(startbuf) ;
}
/*
 *   Copies a string from one place to another; string delimited by
 *   double quotes.
 */
char *cpystr(dest, src)
register char *dest, *src ;
{
   if (*src != '"')
      badline() ;
   else {
      src++ ;
      while (*src != '"' && *src != 0) {
         if (*src == '\\' && src[1] != 0)
            src++ ;
         *dest++ = *src++ ;
      }
      if (*src)
         src++ ;
   }
   *dest = 0 ;
   return(getword(src)) ;
}
/*
 *   Handle a single startup line that's not a comment and non-empty
 *   and been converted to all upper case.
 */
parseline(s, msg)
register char *s ;
struct OURMSG *msg ;
{
   int flags ;
   short t ;
   int key ;
   char *p ;
   register struct hotkey *hk ;

   flags = 0 ;
   if (strncmp(s, "COMMAND", 7)==0) {
      s = getword(s+7) ;
      cpystr(msg->cmd, s) ;
   } else if (strncmp(s, "TIMEOUT", 7)==0) {
      s = getword(s+7) ;
      t = 0 ;
      while ('0' <= *s && *s <= '9')
         t = t * 10 + *s++ - '0' ;
      if (t <= 0)
         t = DEFTIME ;
      msg->interval = t ;
   } else if (strncmp(s, "LINES", 5)==0) {
      msg->draw = 1 ;
   } else if (strncmp(s, "BLANK", 5)==0) {
      msg->draw = -1 ;
   } else {
      if (strncmp(s, "SHIFT", 5)==0) {
         flags = SHIFT ;
         s = getword(s+5) ;
      }
      if (*s == 0)
         badline() ;
      else {
         if (issep(s[1]) &&
             (('A' <= *s && *s <= 'Z') ||
              ('0' <= *s && *s <= '9'))) {
            key = *s ;
            s = getword(s+1) ;
         } else if (strncmp(s, "SPACE", 5)==0) {
            key = ' ' ;
            s = getword(s+5) ;
         } else if (*s == 'F' && ('1' <= s[1] && s[1] <= '9')) {
            s++ ;
            t = *s++ - '0' ;
            if (t == 1 && *s == '0') {
               t = 10 ;
               s++ ;
            }
            s = getword(s) ;
          } else {
            badline() ;
            return ;
         }
         if (strncmp(s, "INTUITION", 9)==0) {
            flags |= NOTCLI ;
            s = getword(s+9) ;
         } else if (strncmp(s, "CLI", 3)==0) {
            flags |= NOTINTUITION ;
            s = getword(s+3) ;
         }
         s = cpystr(upline, s) ;
         p = upline + strlen(upline) + 1 ;
         if (*s)
            cpystr(p, startbuf + (s-upline)) ;
         else
            *p = 0 ;
         t = sizeof(struct hotkey) + strlen(p) + strlen(upline) ;
         hk = AllocMem((long)t, MEMF_CLEAR | MEMF_PUBLIC) ;
         if (hk) {
            hk->key = key ;
            hk->flags = flags ;
            hk->next = hotkeys ;
	    hk->structlen = t ;
	    hk->matchstring = strcpy(hk->strings, upline) ;
	    hk->startstring = strcpy(hk->strings + strlen(upline) + 1, p) ;
            if (hk->startstring[0]==0)
               hk->startstring = NULL ;
            hotkeys = hk ;
         } else
            puts("Out of memory in startup") ;
      }
   }
}
/*
 *   Handle the startup file.
 */
processstartup(s, msg)
char *s ;
struct OURMSG *msg ;
{
   FILE *f ;
   char *p ;

   if (f=fopen(s, "r")) {
      while (fgets(startbuf, MAXSTARTUPLINE, f)) {
         p = getword(startbuf) ;
         if (*p != '*' && *p != '#' && *p != ';' && *p != 0) {
            upcase(upline, p) ;
            parseline(upline, msg) ;
         }
      }
   } else {
      puts("Couldn't open startup file:") ;
      puts(s) ;
   }
}
/*
 *   This stuff down here handles the raw key conversion stuff
 *   properly.  Thanks to Willy Langeveld and Carolyn Scheppner.
 */
char *dos_rkcv();
int  dos_rkcvinit(), dos_rkcvexit();
struct IOStdReq ConStdReq;
/*
 *   This code won't compile under Manx unless you delete the
 *   `ConsoleDevice' function in functions.h, or perform a kludge
 *   like the one I did when I included it.  Why the hell does
 *   Manx have that in there?  If you try to use it, it comes out
 *   undefined, but if you rename the following, it won't link
 *   because it needs the function `ConsoleDevice'.
 */
long ConsoleDevice ;
/**
*
*  Calling sequence:
*  =================
*
*	result = (char *) dos_rkcv(code, buffer, length);
*
*  Description:
*  ============
*
*	Covert raw key number to array of console device ascii text
*	using the default keymap.
*
*  Inputs:
*  =======
*
*	int code		Raw key number.
*	int qual		Qualifier.
*	char *buffer		Pointer to an array of char to receive the
*				conversion.
*	int length		length of buffer.
*
*  Outputs:
*  ========
*
*	F. value:		NULL on conversion failure, or pointer to
*				buffer on success.
*
**/
char *dos_rkcv(code, qual, buffer, length)
int code;
int qual;
char *buffer;
int length;
{
   static struct InputEvent event;

   event.ie_Class = IECLASS_RAWKEY;
   event.ie_Code = code;
   event.ie_Qualifier = qual;

   if (RawKeyConvert(&event, buffer, (long) length, NULL) == 0L) return(0L);

   return(buffer);
}

/**
*
*  Calling sequence:
*  =================
*
*	error = dos_rkcvinit();
*
*  Description:
*  ============
*
*	Open the Console device for later use with dos_rkcv().
*
*  Inputs:
*  =======
*
*	None
*
*  Outputs:
*  ========
*
*	F. value:		1 on failure, zero otherwise.
*				
*
**/
int dos_rkcvinit()
{
   if (OpenDevice("console.device", -1L, &ConStdReq, 0L) != NULL) {
      ConsoleDevice = 0L;
      return(1);
   }
   else {
      ConsoleDevice = (long) ConStdReq.io_Device;
      return(0);
   }
}

/**
*
*  Calling sequence:
*  =================
*
*	error = dos_rkcvexit();
*
*  Description:
*  ============
*
*	Close the Console device after use with dos_rkcv().
*
*  Inputs:
*  =======
*
*	None
*
*  Outputs:
*  ========
*
*	F. value:		Always zero;
*				
**/
int dos_rkcvexit()
{
   if (ConsoleDevice) CloseDevice(&ConStdReq);
   return(0);
}
/*
 *   Set up the key conversion table.  Note that the buffer has to be
 *   long word aligned!
 */
initkeytoasc() {
   register int i ;
   char buf[100] ;

   for (i=0; i<128; i++)
      keytoasc[i] = '.' ;
   if (dos_rkcvinit())
      return(1) ;
   for (i=0; i<128; i++) {
      buf[1] = 0 ;
      if (dos_rkcv(i, 0, buf, 100) && buf[1] == 0 &&
          (buf[0] == ' ' ||
           ('a' <= buf[0] && buf[0] <= 'z' && (buf[0] -= 32)) ||
           ('0' <= buf[0] && buf[0] <= '9'))) {
         keytoasc[i] = buf[0] ;
      }
   }
/*
 *   Have to handle the function keys separately
 */
   for (i=80; i<90; i++)
      keytoasc[i] = i-79 ;
   dos_rkcvexit() ;
   return(0) ;
}
