/*

   ASwarm III V1.0

   ASwarm III by Jake "Wulf" Rose, February '92

   Based upon ASwarm II V1.0 by Markus Illenseer and Matthias Scheler, which
   was based upon Jeff Buterworth's XSwarm

*/

#include <hardware/custom.h>
#include <hardware/dmabits.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <exec/execbase.h>
#include <graphics/displayinfo.h>
#include <graphics/gfxbase.h>
#include <graphics/gfxmacros.h>
#include <intuition/intuitionbase.h>
#include <intuition/gadgetclass.h>
#include <libraries/commodities.h>
#include <libraries/gadtools.h>
#include <dos/dosextens.h>
#include <dos/dostags.h>
#include <utility/tagitem.h>

#include <clib/alib_protos.h>
#include <clib/commodities_protos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <clib/macros.h>

#include <string.h>
#include <stdlib.h>

#ifdef LATTICE /* some stuff for SAS-C */

#include <pragmas/commodities_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/exec_pragmas.h>
#include <pragmas/gadtools_pragmas.h>
#include <pragmas/graphics_pragmas.h>
#include <pragmas/intuition_pragmas.h>

#define VOID_INTERRUPT void __interrupt __saveds
#define REGARGS        __regargs
#define VOID_STDARGS   void __stdargs

UBYTE *VersionString = "$VER: ASwarm III 1.0 (compiled with SAS/C)";

void chkabort(void)

{}

#else /* some stuff for Dice especially for -mR */

#define VOID_INTERRUPT __stkargs __geta4 void
#define REGARGS
#define VOID_STDARGS   __stkargs void

#undef HotKey
#undef ArgArrayInit
#undef ArgInt
#undef ArgString

__stkargs CxObj *HotKey(UBYTE *,struct MsgPort *,long);
__stkargs UBYTE **ArgArrayInit(long arg1,UBYTE **arg2 );
__stkargs LONG ArgInt(UBYTE **arg1,UBYTE *arg2,long arg3 );
__stkargs UBYTE *ArgString(UBYTE **arg1,UBYTE *arg2,UBYTE *arg3);

UBYTE *VersionString = "$VER: ASwarm III 1.0 (compiled with DICE)";

#endif

/*

   Common Definitions

*/

extern struct Custom far custom;

extern struct ExecBase *SysBase;
extern struct DosLibrary *DOSBase;

#define FINDPROCPORT (&((struct Process *)SysBase->ThisTask)->pr_MsgPort)

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;

#define Move(rp,x,y) {(rp)->cp_x=(x); (rp)->cp_y=(y);}

struct Library *CxBase,*GadToolsBase,*IconBase;

/*

   Definitions for our Commodity

*/

struct NewBroker NewBroker =
 {NB_VERSION,"ASwarm III","Amiga Swarm III V1.0",
  "Screen Blanker based on XSwarm", NBU_NOTIFY|NBU_UNIQUE,COF_SHOW_HIDE,
  0,NULL,0};

struct MsgPort *CxPort;

UBYTE *PopKey;
UBYTE *BlankKey;

#define HOTKEY_OPEN_WINDOW 1L
#define HOTKEY_BLANK_SCREEN 2L

#define DEF_CX_PRI 0
#define DEF_POPKEY "control alt s"
#define DEF_BLANKKEY "control alt b"

LONG TimeOut,ClientTimeOut;

#define MAX_TIMEOUT        3600L
#define MAX_CLIENT_TIMEOUT 60L

#define DEF_TIMEOUT        60L
#define DEF_CLIENT_TIMEOUT 5L

#define SERVER_PRI 5L
#define CLIENT_PRI -40L

/*

   Definitions for Swarm Movement

*/

#define BEEACC  4
#define BEEVEL  44
#define WASPACC 20
#define WASPVEL 52
#define BORDER  5

#define BEE_COL_NUM 32

UWORD BeeColors[BEE_COL_NUM] =
 {0x000F,0x004F,0x008F,0x00BF,0x00FF,0x00FB,0x00F7,0x00F3,0x00F0,0x04F0,0x08F0,
  0x09F0,0x0AF0,0x0BF0,0x0CF0,0x0DF0,0x0EF0,0x0FF0,0x0FE0,0x0FD0,0x0FC0,0x0FB0,
  0x0F90,0x0F80,0x0F70,0x0F60,0x0F50,0x0F40,0x0F30,0x0F20,0x0F10,0x0F00};

#define RAND(m) (Random(m)-(m)/2)

UWORD SwarmColors[4] = {0x0000,0x0FFF};
LONG NumWasps,NumBees,Tightness,Speed;

struct SwarmStruct /* structure for a swarm, including the wasps */
 {
  WORD ss_Width;
  WORD ss_Height;
  WORD ss_NumBees;
  WORD ss_NumWasps;
  WORD ss_WaspVel;
  WORD ss_WaspAcc;
  WORD ss_BeeVel;
  WORD ss_BeeAcc;
  WORD *ss_BX[4];    /* the bees */
  WORD *ss_BY[4];
  WORD *ss_WX[4];    /* the wasps */
  WORD *ss_WY[4];
 };

#define ss_BXV ss_BX[3]
#define ss_BYV ss_BY[3]
#define ss_WXV ss_WX[3]
#define ss_WYV ss_WY[3]

#define MAX_WASPS     20L
#define MAX_BEES      100L
#define MAX_SPEED     5L
#define MAX_TIGHTNESS 11L

#define DEF_WASPS     3L
#define DEF_BEES      30L
#define DEF_SPEED     3L
#define DEF_TIGHTNESS 6L

/*

   Definitions for our Blanker Screen

*/

struct NewScreen NewBlankerScreen =
 {0,0,STDSCREENWIDTH,STDSCREENHEIGHT,2,-1,-1,0L,CUSTOMSCREEN|SCREENQUIET,NULL,NULL,NULL,NULL};

struct ModeNode
 {
  struct Node mn_Node;
  UWORD mn_Index;
  ULONG mn_DisplayID;
  char mn_Name[DISPLAYNAMELEN];
 };

struct List *ModeList;
struct ModeNode *DisplayMode;

#define FindMode(l,n) ((struct ModeNode *)FindName(l,n))

#define DEF_MODE HIRES

/*

   Definitions for our configuration window

*/

struct NewWindow NewBlankerWindow =
 {80,16,0,0,0,1,IDCMP_CLOSEWINDOW|IDCMP_REFRESHWINDOW|IDCMP_GADGETDOWN|
  IDCMP_GADGETUP|IDCMP_VANILLAKEY|SLIDERIDCMP|LISTVIEWIDCMP,
  WINDOWCLOSE|WINDOWDRAG|WINDOWDEPTH|SIMPLE_REFRESH,NULL,NULL,NULL,NULL,NULL,
  0,0,0,0,WBENCHSCREEN};
struct Window *BlankerWindow;

#define GID_HIDE      1
#define GID_BLANK     2
#define GID_QUIT      3
#define GID_MODE      4
#define GID_TIMEOUT   5
#define GID_CLIENT    6
#define GID_WASPS     7
#define GID_BEES      8
#define GID_SPEED     9
#define GID_TIGHTNESS 10

#define NUM_GADS 10

struct VisualInfo *BlankerVisualInfo;
struct Gadgets *BlankerGadgets;
struct TextAttr BlankerAttr = {"topaz.font",TOPAZ_EIGHTY,FS_NORMAL,FPF_ROMFONT};

struct NewGadget NewBlankerGadgets[NUM_GADS] =
 {16,101,48,12,"_Hide",&BlankerAttr,GID_HIDE,PLACETEXT_IN,NULL,NULL,
  80,101,48,12,"_Blank",&BlankerAttr,GID_BLANK,PLACETEXT_IN,NULL,NULL,
  144,101,48,12,"_Quit",&BlankerAttr,GID_QUIT,PLACETEXT_IN,NULL,NULL,
  218,17,248,96,"Display Mode",&BlankerAttr,GID_MODE,PLACETEXT_ABOVE,NULL,NULL,
  136,5,66,12,"Timeout",&BlankerAttr,GID_TIMEOUT,PLACETEXT_LEFT,NULL,NULL,
  136,21,66,12,"Client Timeout",&BlankerAttr,GID_CLIENT,PLACETEXT_LEFT,NULL,NULL,
  136,37,66,12,"Tightness    ",&BlankerAttr,GID_TIGHTNESS,PLACETEXT_LEFT,NULL,NULL,
  136,53,66,12,"Speed    ",&BlankerAttr,GID_SPEED,PLACETEXT_LEFT,NULL,NULL,
  136,69,66,12,"Wasps    ",&BlankerAttr,GID_WASPS,PLACETEXT_LEFT,NULL,NULL,
  136,85,66,12,"Bees    ",&BlankerAttr,GID_BEES,PLACETEXT_LEFT,NULL,NULL
  };

UBYTE BlankerGadgetKinds[NUM_GADS] =
 {BUTTON_KIND,BUTTON_KIND,BUTTON_KIND,LISTVIEW_KIND,
  INTEGER_KIND,INTEGER_KIND,SLIDER_KIND,SLIDER_KIND,SLIDER_KIND,SLIDER_KIND};

struct TagItem ButtonGadgetTags[] =
 {GT_Underscore,(ULONG)'_',TAG_DONE,0L};

struct TagItem ListGadgetTags[] =
 {GTLV_Labels,0L,GTLV_Selected,0L,GTLV_ShowSelected,NULL,TAG_DONE,0L};

struct TagItem TimeGadgetTags[] =
 {GTIN_Number,0L,GTIN_MaxChars,4L,TAG_DONE,0L};

struct TagItem ClientGadgetTags[] =
 {GTIN_Number,0L,GTIN_MaxChars,2L,TAG_DONE,0L};

struct TagItem TightnessGadgetTags[] =
 {GTSL_Level,0L,GTSL_Min,1L,GTSL_Max,MAX_TIGHTNESS,GTSL_LevelFormat,0L,
  GTSL_LevelPlace,PLACETEXT_LEFT,GTSL_MaxLevelLen,2L,GA_RELVERIFY,TRUE,
  TAG_DONE,0L};

struct TagItem SpeedGadgetTags[] =
 {GTSL_Level,0L,GTSL_Min,1L,GTSL_Max,MAX_SPEED,GTSL_LevelFormat,0L,
  GTSL_LevelPlace,PLACETEXT_LEFT,GTSL_MaxLevelLen,2L,GA_RELVERIFY,TRUE,
  TAG_DONE,0L};

struct TagItem WaspGadgetTags[] =
 {GTSL_Level,0L,GTSL_Min,1L,GTSL_Max,MAX_WASPS,GTSL_LevelFormat,0L,
  GTSL_LevelPlace,PLACETEXT_LEFT,GTSL_MaxLevelLen,2L,GA_RELVERIFY,TRUE,
  TAG_DONE,0L};

struct TagItem BeeGadgetTags[] =
 {GTSL_Level,0L,GTSL_Min,1L,GTSL_Max,MAX_BEES,GTSL_LevelFormat,0L,
  GTSL_LevelPlace,PLACETEXT_LEFT,GTSL_MaxLevelLen,2L,GA_RELVERIFY,TRUE,
  TAG_DONE,0L};


struct TagItem *BlankerGadgetTagLists[NUM_GADS] =
 {&ButtonGadgetTags[0],
  &ButtonGadgetTags[0],
  &ButtonGadgetTags[0],
  &ListGadgetTags[0],
  &TimeGadgetTags[0],
  &ClientGadgetTags[0],
  &TightnessGadgetTags[0],
  &SpeedGadgetTags[0],
  &WaspGadgetTags[0],
  &BeeGadgetTags[0]
  };

/*

    Definitions for Server/Client Communication

*/

BYTE bsp_TimerSig,bsp_InputSig,bsp_ClientSig;
struct Task *BlankerServerProcess;

#define MASK(n) (1L<<(n))

struct BlankerClientMsg
 {
  struct Message bcm_Message;
  struct Screen *bcm_Screen;
  LONG bcm_Status;
  ULONG bcm_SigMask;
  LONG bcm_Wasps,bcm_Bees,bcm_Speed,bcm_Tightness;
 };

/*

  The following functions are taken from my resource tracker library for SAS/C.
  I normally use to link the programs with this library, but to make it possible
  for other people to compile ASwarmb I included the required source codes.

*/

struct ToolNode
 {
  struct ToolNode *Next;
  void *Tool;
  void (*RemProc)(void *,LONG);
  LONG Size;
 } *ToolList;

void REGARGS RemTool(void *Tool)

{
 struct ToolNode **Ptr,*ToolNode;

 Ptr=&ToolList;
 while (*Ptr)
  if ((*Ptr)->Tool==Tool)
   {
    ToolNode=*Ptr;
    *Ptr=(*Ptr)->Next;
    ToolNode->RemProc(ToolNode->Tool,ToolNode->Size);
    FreeMem (ToolNode,sizeof(struct ToolNode));
   }
  else Ptr=&(*Ptr)->Next;
}

void REGARGS Quit(int ReturnCode)

{
 while (ToolList) RemTool (ToolList->Tool);
 exit (ReturnCode);
}

void REGARGS AddTool(void *NewTool,void *ProcPtr,LONG NewSize)

{
 struct ToolNode *Ptr;
 void (*NewRemProc)(void *,LONG);

 NewRemProc=ProcPtr;
 if (NewTool==NULL) Quit (10);

 if ((Ptr=AllocMem(sizeof(struct ToolNode),MEMF_CLEAR))==NULL)
  {
   NewRemProc (NewTool,NewSize);
   Quit (20);
  }
 Ptr->Next=ToolList;
 Ptr->Tool=NewTool;
 Ptr->RemProc=NewRemProc;
 Ptr->Size=NewSize;
 ToolList=Ptr;
}

/*

   Some useful functions

*/

VOID_STDARGS DeleteMsgPortSafely(struct MsgPort *AnyPort)
{
 struct Message *AnyMsg;

 while (AnyMsg=GetMsg(AnyPort)) ReplyMsg (AnyMsg);
 DeleteMsgPort (AnyPort);
}

int REGARGS ArgIntMax(char **ToolTypes,char *ID,int Default,int Max)
{
 int Val;

 Val=ArgInt(ToolTypes,ID,Default);
 if ((Val<1)||(Val>Max)) return Default;
 else return Val;
}

/*

   Our "InputHandler"

*/

VOID_INTERRUPT BlankerAction(CxMsg *CxMsg,CxObj *CO)

{
 struct InputEvent *IE;

 IE=(struct InputEvent *)CxMsgData(CxMsg);
 if (IE->ie_Class==IECLASS_TIMER) Signal (BlankerServerProcess,1L<<bsp_TimerSig);
 else Signal (BlankerServerProcess,1L<<bsp_InputSig);
}

/*

   Functions for Handling the List of the avaible Graphics Modes

*/

VOID_STDARGS DeleteModeList(struct List *ModeList)

{
 struct ModeNode *ModeNode;

 while (ModeList->lh_Head->ln_Succ)
  {
   ModeNode=(struct ModeNode *)ModeList->lh_Head;
   RemHead (ModeList);
   FreeMem (ModeNode,sizeof(struct ModeNode));
  }
 FreeMem (ModeList,sizeof(struct List));
}

struct List *CreateModeList(void)

{
 struct List *ModeList;
 UWORD Num;
 ULONG DisplayID;
 struct DimensionInfo DimInfo;
 struct NameInfo NameInfo;
 struct ModeNode *ModeNode;

 if (ModeList=AllocMem(sizeof(struct List),MEMF_PUBLIC)) NewList (ModeList);
 else return NULL;

 Num=0;
 DisplayID=INVALID_ID;
 while ((DisplayID=NextDisplayInfo(DisplayID))!=INVALID_ID)
  if ((DisplayID&MONITOR_ID_MASK)&&(ModeNotAvailable(DisplayID)==0L))
   if (GetDisplayInfoData(NULL,(UBYTE *)&DimInfo,sizeof(struct DimensionInfo),DTAG_DIMS,DisplayID))
    if (DimInfo.MaxDepth>1)
     if (GetDisplayInfoData(NULL,(UBYTE *)&NameInfo,sizeof(struct NameInfo),DTAG_NAME,DisplayID))
      if (ModeNode=AllocMem(sizeof(struct ModeNode),MEMF_PUBLIC))
       {
        ModeNode->mn_Node.ln_Name=ModeNode->mn_Name;
        ModeNode->mn_Index=Num++;
        ModeNode->mn_DisplayID=DisplayID;
        strcpy (ModeNode->mn_Name,NameInfo.Name);
        AddTail (ModeList,&ModeNode->mn_Node);
       }

 if (ModeList->lh_Head->ln_Succ) return ModeList;
 else
  {
   FreeMem (ModeList,sizeof(struct List));
   return NULL;
  }
}

struct ModeNode *GetDefaultMode(struct List *ModeList)

{
 struct NameInfo NameInfo;
 struct ModeNode *ModeNode;

 if (GetDisplayInfoData(NULL,(UBYTE *)&NameInfo,sizeof(struct NameInfo),DTAG_NAME,DEF_MODE))
  if (ModeNode=FindMode(ModeList,NameInfo.Name)) return ModeNode;

 return (struct ModeNode *)ModeList->lh_Head;
}

struct ModeNode *GetIndexMode(struct List *ModeList,UWORD Index)

{
 struct ModeNode *ModeNode;

 ModeNode=(struct ModeNode *)ModeList->lh_Head;
 while (ModeNode->mn_Node.ln_Succ)
  if (ModeNode->mn_Index==Index) return ModeNode;
  else ModeNode=(struct ModeNode *)ModeNode->mn_Node.ln_Succ;

 return (struct ModeNode *)ModeList->lh_Head;
}

/*

   Functions for Handling the Configuration Window

*/

LONG GetNum(struct Gadget *Gadget,LONG *Data,LONG Max)
{
 LONG NewData;

 NewData=((struct StringInfo *)Gadget->SpecialInfo)->LongInt;
 if ((NewData<1L)||(NewData>Max))
  {
   GT_SetGadgetAttrs(Gadget,BlankerWindow,NULL,GTIN_Number,(ULONG)*Data,TAG_DONE);
   return FALSE;
  }
 else
  {
   *Data=NewData;
   return TRUE;
  }
}

void CloseBlankerWindow(void)
{
 if (BlankerWindow)
  {
   NewBlankerWindow.LeftEdge=BlankerWindow->LeftEdge;
   NewBlankerWindow.TopEdge=BlankerWindow->TopEdge;

   RemTool (BlankerGadgets);
   RemTool (BlankerVisualInfo);
   RemTool (BlankerWindow);
   BlankerWindow=NULL;
  }
}

void OpenBlankerWindow(void)
{
 struct Gadget *Ptr;
 UWORD Index;
 static char Title[80];

 if (BlankerWindow==NULL)
  {
   strcpy (Title,"ASwarm III: HotKey=");
   strcat (Title,PopKey);

   if (BlankerWindow=OpenWindowTags(&NewBlankerWindow,WA_Title,Title,
       WA_AutoAdjust,TRUE,WA_InnerWidth,472,WA_InnerHeight,118,TAG_DONE))
    {
     AddTool (BlankerWindow,CloseWindow,NULL);

     if ((BlankerVisualInfo=GetVisualInfo(BlankerWindow->WScreen,TAG_DONE))==
         NULL)
      {
       RemTool (BlankerWindow);
       return;
      }
     AddTool (BlankerVisualInfo,FreeVisualInfo,0L);

     BlankerGadgets=NULL;
     if ((Ptr=CreateContext(&BlankerGadgets))==NULL)
      {
       RemTool (BlankerVisualInfo);
       RemTool (BlankerWindow);
       return;
      }
     AddTool (Ptr,FreeGadgets,0L);

     ListGadgetTags[0].ti_Data=(ULONG)ModeList;
     ListGadgetTags[1].ti_Data=(ULONG)DisplayMode->mn_Index;
     TimeGadgetTags[0].ti_Data=(ULONG)TimeOut;
     ClientGadgetTags[0].ti_Data=(ULONG)ClientTimeOut;
     TightnessGadgetTags[0].ti_Data=(ULONG)Tightness;
     TightnessGadgetTags[3].ti_Data=(ULONG)"%2ld";
     SpeedGadgetTags[0].ti_Data=(ULONG)Speed;
     SpeedGadgetTags[3].ti_Data=(ULONG)"%2ld";
     WaspGadgetTags[0].ti_Data=(ULONG)NumWasps;
     WaspGadgetTags[3].ti_Data=(ULONG)"%2ld";
     BeeGadgetTags[0].ti_Data=(ULONG)NumBees;
     BeeGadgetTags[3].ti_Data=(ULONG)"%3ld";

     for (Index=0L; Index<NUM_GADS; Index++)
      {
       NewBlankerGadgets[Index].ng_TopEdge += BlankerWindow->BorderTop;

       NewBlankerGadgets[Index].ng_VisualInfo=BlankerVisualInfo;
       Ptr=CreateGadgetA((ULONG)BlankerGadgetKinds[Index],Ptr,
                        &NewBlankerGadgets[Index],BlankerGadgetTagLists[Index]);
       if (Ptr==NULL)
        {
         CloseBlankerWindow();
         return;
        }

       NewBlankerGadgets[Index].ng_TopEdge -= BlankerWindow->BorderTop;
      }

     AddGList (BlankerWindow,BlankerGadgets,0L,-1L,NULL);
     RefreshGadgets (BlankerGadgets,BlankerWindow,NULL);
     GT_RefreshWindow (BlankerWindow,NULL);
    }
  }

 ScreenToFront (BlankerWindow->WScreen);
 WindowToFront (BlankerWindow);
 ActivateWindow (BlankerWindow);
}

/*

   Function to handle the Commodity Stuff

*/

void REGARGS HandleCxMsg(CxObj *Broker,CxMsg *CxMsg,LONG *TimeUntilBlank,
                         LONG *ThisTimeOut)

{
 ULONG MsgType,MsgID;

 MsgType=CxMsgType(CxMsg);
 MsgID=CxMsgID(CxMsg);
 ReplyMsg ((struct Message *)CxMsg);

 switch (MsgType)
  {
   case CXM_IEVENT:            /* a hotkey was pressed */
    switch (MsgID)
     {
     case HOTKEY_OPEN_WINDOW:
       OpenBlankerWindow();
       break;
     case HOTKEY_BLANK_SCREEN:
       if (*TimeUntilBlank) 
         *TimeUntilBlank = *ThisTimeOut = 2L;
     }
   case CXM_COMMAND:
    switch (MsgID)
     {
      case CXCMD_DISABLE:     /* Message from Exchange (except CXCMD_UNIQUE) */
       (void)ActivateCxObj(Broker,FALSE);
       break;
      case CXCMD_ENABLE:
       (void)ActivateCxObj(Broker,TRUE);
       break;
      case CXCMD_UNIQUE:
      case CXCMD_APPEAR:
       OpenBlankerWindow();
       break;
      case CXCMD_DISAPPEAR:
       CloseBlankerWindow();
       break;
      case CXCMD_KILL:
       Quit (0);
     }
   }
}

/*

   Open a Screen with the supplied DisplayID

*/

void SpritesOff(void)

{
 OFF_SPRITE /* switch sprites off */
 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;
}

struct Screen *CreateScreen(struct List *ModeList,struct ModeNode *ModeNode)

{
 struct Screen *Screen;

 if (Screen=OpenScreenTags(&NewBlankerScreen,SA_DisplayID,ModeNode->mn_DisplayID,TAG_DONE))
  {
   SetRGB4 (&Screen->ViewPort,0,0,0,0);
   SetRast (&Screen->RastPort,0);

   SpritesOff();
  }
 return Screen;
}

/*

   Functions fore Creating/Deleting the Client Process

*/

VOID_STDARGS DeleteBlankerClient(struct MsgPort *BlankerClientPort)

{
 struct BlankerClientMsg BlankerClientMsg;

 BlankerClientMsg.bcm_Message.mn_ReplyPort=FINDPROCPORT;
 PutMsg (BlankerClientPort,(struct Message *)&BlankerClientMsg);

 (void)SetTaskPri(BlankerClientPort->mp_SigTask,SERVER_PRI);

 (void)WaitPort(BlankerClientMsg.bcm_Message.mn_ReplyPort);
 (void)GetMsg(BlankerClientMsg.bcm_Message.mn_ReplyPort);
}

struct MsgPort *REGARGS CreateBlankerClient(void *CodePtr,struct BlankerClientMsg *BlankerClientMsg)

{
 struct Process *BlankerClientProcess;
 struct TagItem ProcTags[3];

 ProcTags[0].ti_Tag=NP_Entry;
 ProcTags[0].ti_Data=(ULONG)CodePtr;
 ProcTags[1].ti_Tag=NP_Name;
 ProcTags[1].ti_Data=(ULONG)"BlankerClient";
 ProcTags[2].ti_Tag=TAG_DONE;

 if (BlankerClientProcess=CreateNewProc(ProcTags))
  {
   BlankerClientMsg->bcm_Message.mn_ReplyPort=FINDPROCPORT;
   PutMsg (&BlankerClientProcess->pr_MsgPort,(struct Message *)BlankerClientMsg);

   (void)WaitPort(BlankerClientMsg->bcm_Message.mn_ReplyPort);
   (void)GetMsg(BlankerClientMsg->bcm_Message.mn_ReplyPort);

   (void)SetTaskPri((struct Task *)BlankerClientProcess,CLIENT_PRI);

   if (BlankerClientMsg->bcm_Status) return &BlankerClientProcess->pr_MsgPort;
  }
 return NULL;
}

/*

   Functions for Creating/Drawing/Removing the Swarm

*/

/* Ill's strange and genius Random Function :-) */

WORD REGARGS Random(WORD Max)

{
 static ULONG Num=0L;
 ULONG Sec,Mic;

 CurrentTime((LONG *)&Sec,(LONG *)&Mic);

 Num*=Sec;
 Num+=Mic;

 while (Num>32767L) Num=Num>>1;

 return (WORD)Num%Max;
}

void REGARGS DeleteSwarm(struct SwarmStruct *Swarm,LONG NumWasps,LONG NumBees)

{
 ULONG Size;

 Size=sizeof(struct SwarmStruct)+sizeof(WORD)*8L*(NumBees+NumWasps);
 FreeMem (Swarm,Size);
}

LONG Insure1(LONG val)
  {
  return ((val==0)? 1:val);   /* return value that is at least 1 */
  }

struct SwarmStruct *REGARGS CreateSwarm(struct Screen *SwarmScreen,
					LONG NumWasps,LONG NumBees,
					LONG Speed,LONG Tightness)

/* allocate memory and initialize the swarm */

{
 ULONG Size;
 LONG Index;
 struct SwarmStruct *Swarm,*SP;
 WORD *Ptr;

 Size=sizeof(struct SwarmStruct)+sizeof(WORD)*8L*(NumBees+NumWasps);

 if ((SP=Swarm=AllocMem(Size,MEMF_CLEAR))==NULL) return NULL;

 SP->ss_NumBees=NumBees;
 SP->ss_NumWasps=NumWasps;
 SP->ss_BeeAcc=Insure1(Tightness*BEEACC/Speed/4)*2+1;
 SP->ss_BeeVel=Insure1(BEEVEL/Speed);
 SP->ss_WaspAcc=Insure1(WASPACC/Speed/2)*2+1;
 SP->ss_WaspVel=Insure1(WASPVEL/Speed);

 SP->ss_Width=SwarmScreen->Width;
 SP->ss_Height=SwarmScreen->Height;

/* point pointers at the proper locations at the end of the memory block */

/* start at the first location after NumBees, NumWasps, Width, and Height */
 Ptr = (WORD *)&(SP[1]);

 for (Index=0L; Index<4L; Index++)
  {
   /* bee pointers */

   SP->ss_BX[Index]=Ptr;
   Ptr+=SP->ss_NumBees;
   SP->ss_BY[Index]=Ptr;
   Ptr+=SP->ss_NumBees;

   /* wasp pointers */
   SP->ss_WX[Index]=Ptr;
   Ptr+=SP->ss_NumWasps;
   SP->ss_WY[Index]=Ptr;
   Ptr+=SP->ss_NumWasps;
  }

/* Bee locations */

 for (Index=0L; Index<SP->ss_NumBees; Index++)
   {
   SP->ss_BX[1][Index]=SP->ss_BX[0][Index]=
                        BORDER+Random(SP->ss_Width-2*BORDER);
   SP->ss_BY[1][Index]=SP->ss_BY[0][Index]=
                        BORDER+Random(SP->ss_Height-2*BORDER);
   SP->ss_BXV[Index]=RAND(SP->ss_BeeAcc);
   SP->ss_BYV[Index]=RAND(SP->ss_BeeAcc);
   }

/* Wasp locations */

 for (Index=0L; Index<SP->ss_NumWasps; Index++)
   {
   SP->ss_WX[1][Index]=SP->ss_WX[0][Index]=
                        BORDER+Random(SP->ss_Width-2*BORDER);
   SP->ss_WY[1][Index]=SP->ss_WY[0][Index]=
                        BORDER+Random(SP->ss_Height-2*BORDER);
   SP->ss_WXV[Index]=RAND(SP->ss_WaspAcc);
   SP->ss_WYV[Index]=RAND(SP->ss_WaspAcc);
   }

 return Swarm;
}

/* move the swarm and redraw it */

void REGARGS DrawSwarm(struct SwarmStruct SP[],struct RastPort *WaspRP,
                       struct RastPort *BeeRP)

{
 LONG WIndex, BIndex;

 /* Wasp */

 for (WIndex=0; WIndex<SP->ss_NumWasps; WIndex++)
   {
   SP->ss_WX[2][WIndex]=SP->ss_WX[1][WIndex];
   SP->ss_WX[1][WIndex]=SP->ss_WX[0][WIndex];
   SP->ss_WY[2][WIndex]=SP->ss_WY[1][WIndex];
   SP->ss_WY[1][WIndex]=SP->ss_WY[0][WIndex];

   SP->ss_WXV[WIndex]+=RAND(SP->ss_WaspAcc);
   SP->ss_WYV[WIndex]+=RAND(SP->ss_WaspAcc);

   if (SP->ss_WXV[WIndex]>SP->ss_WaspVel) SP->ss_WXV[WIndex]=SP->ss_WaspVel;
   if (SP->ss_WXV[WIndex]<-SP->ss_WaspVel) SP->ss_WXV[WIndex]=-SP->ss_WaspVel;
   if (SP->ss_WYV[WIndex]>SP->ss_WaspVel) SP->ss_WYV[WIndex]=SP->ss_WaspVel;
   if (SP->ss_WYV[WIndex]<-SP->ss_WaspVel) SP->ss_WYV[WIndex]=-SP->ss_WaspVel;

   SP->ss_WX[0][WIndex]=SP->ss_WX[1][WIndex]+SP->ss_WXV[WIndex];
   SP->ss_WY[0][WIndex]=SP->ss_WY[1][WIndex]+SP->ss_WYV[WIndex];

   /* Bounce check */

   if ((SP->ss_WX[0][WIndex]<BORDER)||
       (SP->ss_WX[0][WIndex]>SP->ss_Width-BORDER-1)) 
    { 
     SP->ss_WXV[WIndex]=-SP->ss_WXV[WIndex];
     if (SP->ss_WX[0][WIndex]<BORDER) SP->ss_WX[0][WIndex]=BORDER;
     else SP->ss_WX[0][WIndex]=SP->ss_Width-BORDER-1;
    }
   if ((SP->ss_WY[0][WIndex]<BORDER)||
       (SP->ss_WY[0][WIndex]>SP->ss_Height-BORDER-1))
    {
     SP->ss_WYV[WIndex]=-SP->ss_WYV[WIndex];
     if (SP->ss_WY[0][WIndex]<BORDER) SP->ss_WY[0][WIndex]=BORDER;
     else SP->ss_WY[0][WIndex]=SP->ss_Height-BORDER-1;
    } 
   }

 /* Bees */

 /* just a little more randomness */

 SP->ss_BXV[Random(SP->ss_NumBees)]+=RAND(SP->ss_BeeAcc);
 SP->ss_BYV[Random(SP->ss_NumBees)]+=RAND(SP->ss_BeeAcc);

 for (BIndex=0; BIndex<SP->ss_NumBees; BIndex++)
  {
   WORD CDistance=0,CDX,CDY;

   SP->ss_BX[2][BIndex]=SP->ss_BX[1][BIndex];
   SP->ss_BX[1][BIndex]=SP->ss_BX[0][BIndex];
   SP->ss_BY[2][BIndex]=SP->ss_BY[1][BIndex];
   SP->ss_BY[1][BIndex]=SP->ss_BY[0][BIndex];

   for (WIndex=0; WIndex<SP->ss_NumWasps; WIndex++)
     {
     WORD Distance,DX,DY;

     DX=SP->ss_WX[1][WIndex]-SP->ss_BX[1][BIndex];
     DY=SP->ss_WY[1][WIndex]-SP->ss_BY[1][BIndex];
     Distance=ABS(DX)+ABS(DY);
     if (Distance==0) Distance=1;
     if ((CDistance==0) || (Distance<CDistance))
       {
       CDistance=Distance;
       CDX=DX;
       CDY=DY;
       } 
     }

   SP->ss_BXV[BIndex]+=(CDX*SP->ss_BeeAcc)/CDistance;
   SP->ss_BYV[BIndex]+=(CDY*SP->ss_BeeAcc)/CDistance;

   if (SP->ss_BXV[BIndex]>SP->ss_BeeVel) SP->ss_BXV[BIndex]=SP->ss_BeeVel;
   if (SP->ss_BXV[BIndex]<-SP->ss_BeeVel) SP->ss_BXV[BIndex]=-SP->ss_BeeVel;
   if (SP->ss_BYV[BIndex]>SP->ss_BeeVel) SP->ss_BYV[BIndex]=SP->ss_BeeVel;
   if (SP->ss_BYV[BIndex]<-SP->ss_BeeVel) SP->ss_BYV[BIndex]=-SP->ss_BeeVel;

   SP->ss_BX[0][BIndex]=SP->ss_BX[1][BIndex]+SP->ss_BXV[BIndex];
   SP->ss_BY[0][BIndex]=SP->ss_BY[1][BIndex]+SP->ss_BYV[BIndex];

   /* Bounce check */

   if ((SP->ss_BX[0][BIndex]<BORDER)||
       (SP->ss_BX[0][BIndex]>(SP->ss_Width-BORDER-1)))
    {
     SP->ss_BXV[BIndex]=-SP->ss_BXV[BIndex];
     SP->ss_BX[0][BIndex]=SP->ss_BX[1][BIndex]+SP->ss_BXV[BIndex];
    }
   if ((SP->ss_BY[0][BIndex]<BORDER)||
       (SP->ss_BY[0][BIndex]>(SP->ss_Height-BORDER-1)))
    {
     SP->ss_BYV[BIndex]=-SP->ss_BYV[BIndex];
     SP->ss_BY[0][BIndex]=SP->ss_BY[1][BIndex]+SP->ss_BYV[BIndex];
    }
  }

 /* Move the insects */

 WaitTOF();                                 /* synchronize with the display */

 for (WIndex=0; WIndex<SP->ss_NumWasps; WIndex++) /* Wasps */
   {
   SetAPen(WaspRP,0);
   Move(WaspRP,SP->ss_WX[2][WIndex],SP->ss_WY[2][WIndex]);
   Draw(WaspRP,SP->ss_WX[1][WIndex],SP->ss_WY[1][WIndex]);
   SetAPen(WaspRP,1);
   Draw(WaspRP,SP->ss_WX[0][WIndex],SP->ss_WY[0][WIndex]);
   }

 for (BIndex=0; BIndex<SP->ss_NumBees; BIndex++) /* Bees */
   {
   SetAPen (BeeRP,0);
   Move (BeeRP,SP->ss_BX[2][BIndex],SP->ss_BY[2][BIndex]);
   Draw (BeeRP,SP->ss_BX[1][BIndex],SP->ss_BY[1][BIndex]);
   SetAPen (BeeRP,1);
   Draw (BeeRP,SP->ss_BX[0][BIndex],SP->ss_BY[0][BIndex]);
   }
}

/*

   This is the Client Process's Main Loop

*/

VOID_INTERRUPT ASwarmClientProcess(void)

{
 struct BlankerClientMsg *BlankerClientMsg;
 struct MsgPort *BlankerClientPort;
 struct Task *BlankerServerTask;
 ULONG BlankerServerSigMask;
 struct Screen *SwarmScreen;
 LONG Index,NumWasps,NumBees,Speed,Tightness;
 struct SwarmStruct *Swarm;
 WORD Color,DColor;
 static struct RastPort RastPorts[2];
 static struct BitMap BitMaps[2];

 /* wait for Server's initial Message */

 BlankerClientPort=FINDPROCPORT;
 (void)WaitPort(BlankerClientPort);
 BlankerClientMsg=(struct BlankerClientMsg *)GetMsg(BlankerClientPort);

 BlankerServerTask=BlankerClientMsg->bcm_Message.mn_ReplyPort->mp_SigTask;
 BlankerServerSigMask=BlankerClientMsg->bcm_SigMask;

 NumWasps=BlankerClientMsg->bcm_Wasps;
 NumBees=BlankerClientMsg->bcm_Bees;
 Tightness=BlankerClientMsg->bcm_Tightness;
 Speed=(MAX_SPEED+1-BlankerClientMsg->bcm_Speed);
 SwarmScreen=BlankerClientMsg->bcm_Screen;

 /* initialize swarm with requested speed and numbers of wasps and bees */
 if ((Swarm=CreateSwarm(SwarmScreen,NumWasps,NumBees,Speed,Tightness))==NULL)
  {
   BlankerClientMsg->bcm_Status=FALSE;
   Forbid();
   ReplyMsg ((struct Message *)BlankerClientMsg);
   return;
  }
 BlankerClientMsg->bcm_Status=TRUE;
 ReplyMsg ((struct Message *)BlankerClientMsg);

 /* Here we do a little trick:
    We create two RastPorts - one for the Wasp(s), one for the Bees - with one
    BitPlane. So we have only to draw in one BitPlane and are able to do it 
    faster. */

 for (Index=0L; Index<2L; Index++)
   {
   InitRastPort (&RastPorts[Index]);
   RastPorts[Index].BitMap=&BitMaps[Index];
   InitBitMap (&BitMaps[Index],1L,SwarmScreen->Width,SwarmScreen->Height);
   BitMaps[Index].Planes[0]=SwarmScreen->BitMap.Planes[Index];
   SetDrMd(&RastPorts[Index],JAM1);
   }

 Color=-1;
 DColor=1;
 while ((BlankerClientMsg=
        (struct BlankerClientMsg *)GetMsg(BlankerClientPort))==NULL)
  {

   /* Color Cycling */

   Color+=DColor;
   if (Color==BEE_COL_NUM)
    {
     Color-=2;
     DColor=-1;
    }
   else
    if (Color<0)
     {
      Color+=2;
      DColor=1;
     }
   SwarmColors[2]=BeeColors[Color];
   LoadRGB4 (&SwarmScreen->ViewPort,SwarmColors,4);

   /* Move the Swarm */
   DrawSwarm (Swarm,&RastPorts[0],&RastPorts[1]);

   Signal (BlankerServerTask,BlankerServerSigMask);
  }

 /* We are requested to finish, so we do. */

 DeleteSwarm (Swarm,NumWasps,NumBees);
 Forbid();
 ReplyMsg ((struct Message *)BlankerClientMsg);
}

/*

   The Main Loop

*/

void main(LONG argc,UBYTE *argv[])

{
 char **ToolTypes;
 CxObj *Broker,*ObjectList,*Filter;
 struct IntuiMessage *IntMsg;
 CxMsg *BlankerCxMsg;
 LONG ThisTimeOut,TimeUntilBlank,TimeUntilBlack;
 struct Screen *BlankerScreen=NULL;
 struct BlankerClientPort *BlankerClientPort=NULL;

 /* open our Libraries */

 AddTool (GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",37L),
          CloseLibrary,0L);
 AddTool (IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",
          37L),CloseLibrary,0L);
 AddTool (IconBase=OpenLibrary("icon.library",37L),CloseLibrary,0L);
 AddTool (CxBase=OpenLibrary("commodities.library",37L),CloseLibrary,0L);
 AddTool (GadToolsBase=OpenLibrary("gadtools.library",37L),CloseLibrary,0L);

 /* create List of Graphics Modes */

 AddTool (ModeList=CreateModeList(),DeleteModeList,0L);

 /* get our Arguments */

 if (ToolTypes=ArgArrayInit(argc,argv)) AddTool (ToolTypes,ArgArrayDone,0L);

 /* get some Signals */

 BlankerServerProcess=SysBase->ThisTask;
 if ((bsp_TimerSig=AllocSignal(-1L))==-1) Quit (10);
 AddTool ((void *)bsp_TimerSig,FreeSignal,0L);
 if ((bsp_InputSig=AllocSignal(-1L))==-1) Quit (10);
 AddTool ((void *)bsp_InputSig,FreeSignal,0L);
 if ((bsp_ClientSig=AllocSignal(-1L))==-1) Quit (10);
 AddTool ((void *)bsp_ClientSig,FreeSignal,0L);

 /* initialize our Broker = install us as a Commodity */

 AddTool (CxPort=CreateMsgPort(),DeleteMsgPortSafely,0L);

 NewBroker.nb_Pri=ArgInt(ToolTypes,"CX_PRIORITY",DEF_CX_PRI);
 NewBroker.nb_Port=CxPort;
 AddTool (Broker=CxBroker(&NewBroker,NULL),DeleteCxObjAll,0L);

 /* get Time Out, Client Time Out and Display mode */

 TimeOut=ArgIntMax(ToolTypes,"TIMEOUT",DEF_TIMEOUT,MAX_TIMEOUT);
 ClientTimeOut=ArgIntMax(ToolTypes,"CLIENTTIMEOUT",DEF_CLIENT_TIMEOUT,MAX_CLIENT_TIMEOUT);
 if ((DisplayMode=FindMode(ModeList,ArgString(ToolTypes,"DISPLAY","")))==NULL)
  DisplayMode=GetDefaultMode(ModeList);

 /* get Parameters for Swarm Movement */

 NumWasps=ArgIntMax(ToolTypes,"WASPS",DEF_WASPS,MAX_WASPS);
 NumBees=ArgIntMax(ToolTypes,"BEES",DEF_BEES,MAX_BEES);
 Speed=ArgIntMax(ToolTypes,"SPEED",DEF_SPEED,MAX_SPEED);
 Tightness=ArgIntMax(ToolTypes,"TIGHTNESS",DEF_TIGHTNESS,MAX_TIGHTNESS);

 /* install our hot keys */

 PopKey=ArgString(ToolTypes,"CX_POPKEY",DEF_POPKEY);
 BlankKey=ArgString(ToolTypes,"BLANKKEY",DEF_BLANKKEY);

 if ((Filter=HotKey(PopKey,CxPort,HOTKEY_OPEN_WINDOW))==NULL) Quit (10);
 else AttachCxObj(Broker,Filter);
 if (CxObjError(Filter)) Quit (10);

 if ((Filter=HotKey(BlankKey,CxPort,HOTKEY_BLANK_SCREEN))==NULL) Quit (10);
 else AttachCxObj(Broker,Filter);
 if (CxObjError(Filter)) Quit (10);

 /* install our "InputHandler" */

 ObjectList=CxCustom(BlankerAction,0L);
 AttachCxObj (Broker,ObjectList);
 if (CxObjError(ObjectList)) Quit (10);

 (void)ActivateCxObj(Broker,TRUE);
 AddTool (Broker,ActivateCxObj,FALSE);

 /* open Window on startup if not forbidden */

 if (stricmp(ArgString(ToolTypes,"CX_POPUP",""),"NO")) OpenBlankerWindow();

 /* increase our Priority */

 AddTool(FindTask(NULL),SetTaskPri,(LONG)SetTaskPri(FindTask(NULL),SERVER_PRI));

 /* start the Loop */

 TimeUntilBlank=ThisTimeOut=10L*TimeOut;
 TimeUntilBlack=10L*ClientTimeOut;

 FOREVER
  {
   ULONG Mask;

   if (BlankerWindow)
    Mask=Wait(MASK(bsp_TimerSig)|MASK(bsp_InputSig)|MASK(bsp_ClientSig)|
              MASK(CxPort->mp_SigBit)|MASK(BlankerWindow->UserPort->mp_SigBit)|
              SIGBREAKF_CTRL_C);
   else
    Mask=Wait(MASK(bsp_TimerSig)|MASK(bsp_InputSig)|MASK(bsp_ClientSig)|
              MASK(CxPort->mp_SigBit)|SIGBREAKF_CTRL_C);

   /* process Window Events */

   while ((BlankerWindow!=NULL)&&(IntMsg=GT_GetIMsg(BlankerWindow->UserPort)))
    switch (IntMsg->Class)
     {
      struct Gadget *Clicked;
      UWORD Code;

      case IDCMP_CLOSEWINDOW:
       GT_ReplyIMsg (IntMsg);
       CloseBlankerWindow();
       break;
      case IDCMP_REFRESHWINDOW:
       GT_BeginRefresh (BlankerWindow);
       GT_EndRefresh (BlankerWindow,TRUE);
       break;
      case IDCMP_GADGETUP:
       Code=IntMsg->Code;
       Clicked=(struct Gadget *)IntMsg->IAddress;
       GT_ReplyIMsg (IntMsg);
       switch (Clicked->GadgetID)
        {
         case GID_HIDE:
          CloseBlankerWindow();
          break;
         case GID_QUIT:
          Quit (0);
         case GID_BLANK:
          if (TimeUntilBlank) TimeUntilBlank=ThisTimeOut=2L;
          break;
         case GID_MODE:
          DisplayMode=GetIndexMode(ModeList,Code);
          break;
         case GID_TIMEOUT:
          if (GetNum(Clicked,&TimeOut,MAX_TIMEOUT)) TimeUntilBlank=ThisTimeOut=10L*TimeOut;
          break;
         case GID_CLIENT:
          if (GetNum(Clicked,&ClientTimeOut,MAX_CLIENT_TIMEOUT)) TimeUntilBlack=10L*ClientTimeOut;
          break;
         case GID_WASPS:
          NumWasps = Code;
          break;
         case GID_BEES:
          NumBees = Code;
          break;
         case GID_SPEED:
          Speed = Code;
	  break;
	 case GID_TIGHTNESS:
	  Tightness = Code;
        }
       break;
      case IDCMP_VANILLAKEY:
       Code=IntMsg->Code;
       GT_ReplyIMsg (IntMsg);
       switch ((char)Code)
        {
         case 'H':
         case 'h':
          CloseBlankerWindow();
          break;
         case 'Q':
         case 'q':
          Quit (0);
         case 'B':
         case 'b':
          if (TimeUntilBlank) TimeUntilBlank=ThisTimeOut=2L;
        }
       break;
      default:
       GT_ReplyIMsg (IntMsg);
     }

   /* process Commodity Messages */

   while (BlankerCxMsg=(CxMsg *)GetMsg(CxPort)) 
         HandleCxMsg (Broker,BlankerCxMsg,&TimeUntilBlank,&ThisTimeOut);

   /* check for <CTRL>-C */

   if (Mask&SIGBREAKF_CTRL_C) Quit (0);

   /* Input detected, unblank if necessary */

   if (Mask&MASK(bsp_InputSig))
    {
     if (TimeUntilBlank==0L)
      {
       ON_SPRITE
       if (BlankerClientPort) RemTool (BlankerClientPort);
       RemTool (BlankerScreen);
       ThisTimeOut=10L*TimeOut;
      }
     TimeUntilBlank=ThisTimeOut;
    }

   /* client has confirmed that it is still alive */

   if (Mask&MASK(bsp_ClientSig))
    {
     ON_DISPLAY
     TimeUntilBlack=10L*ClientTimeOut;
    }

   /* 1/10 sec is over */

   if (Mask&MASK(bsp_TimerSig))
    if (TimeUntilBlank)
     {
      TimeUntilBlank--;
      if (TimeUntilBlank==0L) /* Time Out reached, blank the screen */
       {
        struct BlankerClientMsg BlankerClientMsg;

        AddTool (BlankerScreen=CreateScreen(ModeList,DisplayMode),CloseScreen,0L);

        BlankerClientMsg.bcm_Screen=BlankerScreen;
        BlankerClientMsg.bcm_SigMask=1L<<bsp_ClientSig;
        BlankerClientMsg.bcm_Wasps=NumWasps;
        BlankerClientMsg.bcm_Bees=NumBees;
        BlankerClientMsg.bcm_Speed=Speed;
	BlankerClientMsg.bcm_Tightness=Tightness;

        if (BlankerClientPort=CreateBlankerClient(ASwarmClientProcess,&BlankerClientMsg))
         {
          TimeUntilBlack=10L*ClientTimeOut; /* try to start Client */
          AddTool (BlankerClientPort,DeleteBlankerClient,0L);
         }
       }
     }
    else
     {
      if (IntuitionBase->FirstScreen!=BlankerScreen)
       {
        ScreenToFront (BlankerScreen);
        SpritesOff();
       }
      if (TimeUntilBlack)
       {
        TimeUntilBlack--;
        if (TimeUntilBlack==0L) OFF_DISPLAY /* Client Time Out reached, turn entire screen black */
       }
     }
  }
}
