/*

   ASwarm II V1.2a - The Original One

   Written by Markus "Ill" Illenseer    markus@Techfak.Uni-Bielefeld.de
   and Matthias "Tron" Scheler          tron@uni-paderborn.de

   Based upon Jeff Buterworths XSwarm.

   Please leave this source intact. ASwarm is freely distributable,
   but no longer 'Public Domain'. If you have suggestions or Bug-Reports,
   please contact us.

   Read the Manuals for known Bugs and Contact-Adresses.

*/

#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 <workbench/startup.h>
#include <workbench/icon.h>
#include <workbench/workbench.h>
#include <dos/dosextens.h>
#include <dos/dostags.h>
#include <dos/rdargs.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/icon_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/icon_pragmas.h>
#include <pragmas/intuition_pragmas.h>

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

CxObj *HotKey(UBYTE *,struct MsgPort *,long);
void NewList(struct List *);

UBYTE *VersionString = "$VER: ASwarm II 1.2a (compiled with SAS/C)";
/* Attention ! ASwarm<ALT SPACE>II 1.2a ... */

void chkabort(void)
{}

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

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

__stkargs CxObj *HotKey(UBYTE *,struct MsgPort *,long);
__stkargs void NewList(struct List *);

UBYTE *VersionString = "$VER: ASwarm II 1.2a (compiled with DICE)";
/* Attention ! ASwarm<ALT SPACE>II 1.2a ... */

void main(LONG,UBYTE **);

void wbmain(struct WBStartup *WBS)

{
 if (WBS->sm_NumArgs) (void)CurrentDir(WBS->sm_ArgList->wa_Lock);

 main(0L,(UBYTE **)WBS);
}

#endif

/*

   Common Definitions

*/

#define custom (*((struct Custom *)0xDFF000L))

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

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

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;

struct Library *CxBase,*GadToolsBase,*IconBase;

LONG WBStarted;

#define NUMTOOLTYPES 13

char *ToolTypeIDs[NUMTOOLTYPES] =
 {"CX_PRIORITY","CX_POPUP","CX_POPKEY","BLANKKEY",
  "TIMEOUT","CLIENTTIMEOUT",
  "DISPLAY","CYCLE","AIM",
  "WASP","BEES","SPEED","TIGHTNESS"};

/*

   Definitions for our Commodity

*/

struct NewBroker NewBroker =
 {NB_VERSION,"ASwarm II","Amiga Swarm II V1.2a","Screen Blanker based on XSwarm",
  NBU_NOTIFY|NBU_UNIQUE,COF_SHOW_HIDE,0,NULL,0};
CxObj *Broker,*PopKeyFilter,*BlankKeyFilter;
struct MsgPort *CxPort;
LONG CxPri,CxPopUp;

UBYTE PopKey[32],BlankKey[32];

#define EVENT_OPEN_WINDOW 1L
#define EVENT_BLANK       2L

#define DEF_CX_PRI 0L
#define DEF_POPKEY   "CTRL ALT s"     /* Hot Key for the Edit Window */
#define DEF_BLANKKEY "CTRL ALT b"     /* Hot Key for immediate Blank */

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                 /* Don't change this, saves live */
#define CLIENT_PRI -40L

/*

   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
#define DEF_MODE_NAME "HighRes"

/* Mini and Maximum definitions for possible parameters */

#define MAX_SPEED     4L
#define MAX_WASPS     10L
#define MAX_BEES      500L
#define MAX_TIGHTNESS 10L

#define DEF_SPEED     4L
#define DEF_WASPS     2L
#define DEF_BEES      25L
#define DEF_TIGHTNESS 5L

char *Template = /* extra extra extra long :-) */
 "CX_PRIORITY/N/K,CX_POPKEY/K,CX_POPUP/S,BLANKKEY/K,SECONDS=TIMEOUT/N/K,"
 "CLIENTTIMEOUT/N/K,DISPLAY/K,CYCLE/S,AIM/S,WASPS/N/K,BEES/N/K,SPEED/N/K,TIGHTNESS/N/K";

/*

   Definitions for our Configuration Window

*/

WORD LeftEdge=68,TopEdge=13;     /* Hard Coded, the window is too big to */
struct Window *BlankerWindow;    /* let it open under the mouse pointer */

#define GID_HIDE      0  /* The Gadget-ID's */
#define GID_BLANK     1
#define GID_QUIT      2
#define GID_MODE      3
#define GID_TIMEOUT   4
#define GID_CLIENT    5
#define GID_SPEED     6
#define GID_WASPS     7
#define GID_BEES      8
#define GID_TIGHT     9
#define GID_CYCLE    10
#define GID_AIMMODE  11
#define GID_POPKEY   12
#define GID_BLANKKEY 13

#define NUM_GADS 14

char *SpeedNames[] = /* List of Speed Modes */
 {"Slow Motion","Very Slow","Slow","Normal","Fast","Very Fast",
  "Incredible",NULL};

struct VisualInfo *BlankerVisualInfo;
struct Menu *BlankerMenu;
struct Gadget *BlankerContext,*BlankerGadgets[NUM_GADS];
struct TextAttr BlankerAttr={"topaz.font",TOPAZ_EIGHTY,FS_NORMAL,FPF_ROMFONT};

struct NewMenu NewBlankerMenus[] =
 {NM_TITLE,"Project",NULL,0,0L,NULL,
  NM_ITEM,"Load Config","L",0,0L,NULL,
  NM_ITEM,"Save Config","S",0,0L,NULL,
  NM_ITEM,NM_BARLABEL,NULL,0,0L,NULL,
  NM_ITEM,"About ...",NULL,0,0L,NULL,
  NM_ITEM,NM_BARLABEL,NULL,0,0L,NULL,
  NM_ITEM,"Hide","H",0,0L,NULL,
  NM_ITEM,"Quit","Q",0,0L,NULL,
  NM_TITLE,"Edit",NULL,0,0L,NULL,
  NM_ITEM,"Reset To Defaults","D",0,0L,NULL,
  NM_END,NULL,NULL,0,0L,NULL};

struct NewGadget NewBlankerGadgets[NUM_GADS] =
 {16,147,48,12,"_Hide",&BlankerAttr,GID_HIDE,PLACETEXT_IN,NULL,NULL,
  90,147,48,12,"_Blank",&BlankerAttr,GID_BLANK,PLACETEXT_IN,NULL,NULL,
  164,147,48,12,"_Quit",&BlankerAttr,GID_QUIT,PLACETEXT_IN,NULL,NULL,
  224,55,272,104,"Display Mode",&BlankerAttr,GID_MODE,PLACETEXT_ABOVE,NULL,NULL,
  156,10,56,12,"_Timeout",&BlankerAttr,GID_TIMEOUT,PLACETEXT_LEFT,NULL,NULL,
  156,24,56,12,"C_lient Timeout",&BlankerAttr,GID_CLIENT,
                                              PLACETEXT_LEFT,NULL,NULL,
  92,55,120,12,"_Speed",&BlankerAttr,GID_SPEED,PLACETEXT_LEFT,NULL,NULL,
  112,69,100,12,"_Wasps       ",&BlankerAttr,GID_WASPS,PLACETEXT_LEFT,NULL,NULL,
  112,83,100,12,"B_ees        ",&BlankerAttr,GID_BEES,PLACETEXT_LEFT,NULL,NULL,
  112,97,100,12,"T_ightness   ",&BlankerAttr,GID_TIGHT,PLACETEXT_LEFT,NULL,NULL,
  186,112,16,12,"_Color Cycling",&BlankerAttr,GID_CYCLE,
                                              PLACETEXT_LEFT,NULL,NULL,
  186,126,16,12,"_Aim Mode",&BlankerAttr,GID_AIMMODE,PLACETEXT_LEFT,NULL,NULL,
  308,10,188,12,"_Pop Key",&BlankerAttr,GID_POPKEY,PLACETEXT_LEFT,NULL,NULL,
  308,24,188,12,"Blank _Key",&BlankerAttr,GID_BLANKKEY,
                                              PLACETEXT_LEFT,NULL,NULL};
UBYTE BlankerGadgetKinds[NUM_GADS] =
 {BUTTON_KIND,BUTTON_KIND,BUTTON_KIND,LISTVIEW_KIND,
  INTEGER_KIND,INTEGER_KIND,CYCLE_KIND,SLIDER_KIND,SLIDER_KIND,SLIDER_KIND,
  CHECKBOX_KIND,CHECKBOX_KIND,STRING_KIND,STRING_KIND};
struct TagItem BlankerGadgetTags[] =
 {GTLV_Labels,0L,GTLV_Selected,0L,GTLV_ShowSelected,NULL,TAG_DONE,0L,
  GTIN_Number,0L,GTIN_MaxChars,4L,TAG_DONE,0L,
  GTIN_Number,0L,GTIN_MaxChars,2L,TAG_DONE,0L,
  GTCY_Labels,NULL,GTCY_Active,0,TAG_DONE,0L,
  GTSL_Min,1L,GTSL_Max,MAX_WASPS,GTSL_Level,0L,
  GTSL_MaxLevelLen,2L,GTSL_LevelFormat,(ULONG)"%2ld",
  GA_RelVerify,TRUE,PGA_Freedom,LORIENT_HORIZ,TAG_DONE,0L,
  GTSL_Min,1L,GTSL_Max,MAX_BEES,GTSL_Level,0L,
  GTSL_MaxLevelLen,3L,GTSL_LevelFormat,(ULONG)"%3ld",
  GA_RelVerify,TRUE,PGA_Freedom,LORIENT_HORIZ,TAG_DONE,0L,
  GTSL_Min,1L,GTSL_Max,MAX_TIGHTNESS,GTSL_Level,0L,
  GTSL_MaxLevelLen,2L,GTSL_LevelFormat,(ULONG)"%2ld",
  GA_RelVerify,TRUE,PGA_Freedom,LORIENT_HORIZ,TAG_DONE,0L,
  GTCB_Checked,0L,TAG_DONE,0L,
  GTCB_Checked,0L,TAG_DONE,0L,
  GTST_String,0L,GTST_MaxChars,31L,TAG_DONE,0L,
  GTST_String,0L,GTST_MaxChars,31L,TAG_DONE,0L};
UBYTE BlankerTagsIndex[NUM_GADS] = {3,3,3,0,4,7,10,13,21,29,37,39,41,44};

struct EasyStruct AboutEasyStruct =
 {sizeof(struct EasyStruct),0L,"About ...",
  "ASwarm II V1.2a - The Original One\n"
  "© 1992 by Markus Illenseer and Matthias Scheler\n"
  "This program is freely distributable.","Ok"};

/*

    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;
  LONG bcm_Cycle,bcm_AimMode;
 } BlankerClientMsg;

/*

   Definitions or Swarm Movement

*/

#define BEEACC  3
#define BEEVEL  9
#define WASPACC 5
#define WASPVEL 13
#define BORDER  5

#define FIXED    0

#define BEE_COL_NUM 32

UWORD BeeColors[BEE_COL_NUM] = /* Color Cycling Table */
 {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,0x0000,0x0FFF};
LONG NumWasps,NumBees,Speed,Tightness;
LONG Cycle,AimMode;
LONG ThisTimeOut,TimeUntilBlank;

struct SwarmStruct /* structure for a swarm, including the wasp */
 {
  WORD ss_Width;   /* Width and */
  WORD ss_Height;  /* Height of the used Screen, probably useful for
                      future versions of ASwarm :) */
  WORD ss_NumWasps;/* total number of the Wasps */
  WORD *ss_WX[4];  /* The Wasps x-Position*/  /* WX[3] is used for Velocity */
  WORD *ss_WY[4];  /* Y-Position */
  WORD *ss_NB;     /* No. of Bees following this Wasp */
  WORD ss_NumBees; /* Total (!) number of Bees */
  WORD ss_BeeAcc;  /* Acceleration of the Bees */
  WORD *ss_X[4];   /* The Bees X-Position */  /* X[3] used for Velocity */
  WORD *ss_Y[4];   /* Y-Position */
  WORD *ss_MW;     /* The aimed Wasp */
 };

/* Ill's strange Macros for easy access to the above structure */

#define BXVel(I)   (SP->ss_X[3][I])
#define BYVel(I)   (SP->ss_Y[3][I])
#define BeeX(P,I)  (SP->ss_X[P][I])
#define BeeY(P,I)  (SP->ss_Y[P][I])
#define MyWasp(I)  (SP->ss_MW[I])

#define WaXVel(I)  (SP->ss_WX[3][I])
#define WaYVel(I)  (SP->ss_WY[3][I])
#define WaspX(P,I) (SP->ss_WX[P][I])
#define WaspY(P,I) (SP->ss_WY[P][I])

/*

    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 ASwarm II included the required
    source codes.

*/

struct ToolNode
 {
  struct ToolNode *Next;
  void *Tool;
  void (*RemProc)(void *,LONG); /* requires stack arguments !!! */
  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);
    FreeVec ((APTR)ToolNode);

    return; /* This one was missing in ASwarm II V1.0-V1.1 and in
               ASwarm III :-) */
   }
  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=AllocVec(sizeof(struct ToolNode),0L))==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);
}

LONG RDArgsLong(LONG Param,LONG Default,LONG Min,LONG Max)

{
 LONG *Ptr;

 if ((Ptr=(LONG *)Param)==NULL) return Default;

 if ((*Ptr<Min)||(*Ptr>Max)) return Default;
 else return *Ptr;
}

UWORD PutChar[2] = {0x16C0,0x4E75};

/* dirty hack to avoid assembler part :-)

   16C0: move.b d0,(a3)+
   4E75: rts
*/

VOID_STDARGS SPrintF(char *Buffer,char *FormatString,...)

{
 RawDoFmt (FormatString,(APTR)((LONG *)&FormatString+1L),
           (void *)PutChar,Buffer);
}

/*

   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);
   FreeVec ((APTR)ModeNode);
  }
 FreeVec ((APTR)ModeList);
}

struct List *CreateModeList(void)

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

 if (ModeList=AllocVec(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=AllocVec(sizeof(struct ModeNode),MEMF_PUBLIC))
       {
        (void)strcpy(ModeNode->mn_Node.ln_Name=ModeNode->mn_Name,
                     NameInfo.Name);
        ModeNode->mn_Index=Num++;
        ModeNode->mn_DisplayID=DisplayID;
        AddTail (ModeList,&ModeNode->mn_Node);
       }

 if (ModeList->lh_Head->ln_Succ==NULL)
  if (ModeNode=AllocVec(sizeof(struct ModeNode),MEMF_PUBLIC))
   {
    (void)strcpy(ModeNode->mn_Node.ln_Name=ModeNode->mn_Name,DEF_MODE_NAME);
    ModeNode->mn_Index=Num;
    ModeNode->mn_DisplayID=DEF_MODE;
    AddTail (ModeList,&ModeNode->mn_Node);
   }
  else
   {
    FreeVec ((APTR)ModeList);
    return NULL;
   }

 return ModeList;
}

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;
}

/*

   "icon.library" Stuff

*/

char REGARGS *YesNo(LONG Flag)

{
 return Flag?"YES":"NO";
}

char REGARGS *ToolTypeString(struct DiskObject *DiskObject,char *ID,
                             char *Default)

{
 char *String;

 if (DiskObject==NULL) return Default;

 if (String=FindToolType(DiskObject->do_ToolTypes,ID)) return String;
 else return Default;
}

LONG REGARGS ToolTypeBoolean(struct DiskObject *DiskObject,char *ID,
                             LONG Default)

{
 if (Default) return (stricmp(ToolTypeString(DiskObject,ID,""),
                              YesNo(FALSE))!=0);
 else return (stricmp(ToolTypeString(DiskObject,ID,""),YesNo(TRUE))==0);
}

LONG REGARGS ToolTypeLong(struct DiskObject *DiskObject,char *ID,LONG Default,
                          LONG Min,LONG Max)

{
 char *String;

 if (DiskObject==NULL) return Default;

 if (String=FindToolType(DiskObject->do_ToolTypes,ID))
  {
   LONG Value;

   Value=atol(String);
   if ((Value<Min)||(Value>Max)) return Default;
   else return Value;
  }
 else return Default;
}

void REGARGS LoadConfig(char *Name)

{
 struct DiskObject *DiskObject;

 if (Name) DiskObject=GetDiskObject(Name);
 else DiskObject=NULL;

 CxPri=ToolTypeLong(DiskObject,ToolTypeIDs[0],0L,-128L,127L);
 CxPopUp=ToolTypeBoolean(DiskObject,ToolTypeIDs[1],TRUE);
 (void)strcpy(PopKey,ToolTypeString(DiskObject,ToolTypeIDs[2],DEF_POPKEY));
 (void)strcpy(BlankKey,ToolTypeString(DiskObject,ToolTypeIDs[3],DEF_BLANKKEY));

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

 if ((TimeOut=ToolTypeLong(DiskObject,"SECONDS",0L,1L,MAX_TIMEOUT))==0L)
  TimeOut=ToolTypeLong(DiskObject,ToolTypeIDs[4],DEF_TIMEOUT,1L,MAX_TIMEOUT);

 ClientTimeOut=ToolTypeLong(DiskObject,ToolTypeIDs[5],
                            DEF_CLIENT_TIMEOUT,1L,MAX_CLIENT_TIMEOUT);
 if ((DisplayMode=FindMode(ModeList,ToolTypeString(DiskObject,ToolTypeIDs[6],
                                                   "")))==NULL)
  DisplayMode=GetDefaultMode(ModeList);

 /* get Parameters for Graphics */

 Cycle=ToolTypeBoolean(DiskObject,ToolTypeIDs[7],TRUE);
 AimMode=ToolTypeBoolean(DiskObject,ToolTypeIDs[8],FALSE);

 NumWasps=ToolTypeLong(DiskObject,ToolTypeIDs[9],DEF_WASPS,1L,MAX_WASPS);
 NumBees=ToolTypeLong(DiskObject,ToolTypeIDs[10],DEF_BEES,1L,MAX_BEES);
 Speed=ToolTypeLong(DiskObject,ToolTypeIDs[11],DEF_SPEED,1L,MAX_SPEED);
 Tightness=ToolTypeLong(DiskObject,ToolTypeIDs[12],DEF_TIGHTNESS,1L,
                        MAX_TIGHTNESS);

 if (BlankerWindow)
  {
   GT_SetGadgetAttrs (BlankerGadgets[GID_TIMEOUT],BlankerWindow,NULL,
                      GTIN_Number,TimeOut,TAG_DONE);
   GT_SetGadgetAttrs (BlankerGadgets[GID_CLIENT],BlankerWindow,NULL,
                      GTIN_Number,ClientTimeOut,TAG_DONE);

   GT_SetGadgetAttrs (BlankerGadgets[GID_MODE],BlankerWindow,NULL,
                      GTLV_Selected,DisplayMode->mn_Index,TAG_DONE);

   GT_SetGadgetAttrs (BlankerGadgets[GID_CYCLE],BlankerWindow,NULL,
                      GTCB_Checked,Cycle,TAG_DONE);
   GT_SetGadgetAttrs (BlankerGadgets[GID_AIMMODE],BlankerWindow,NULL,
                      GTCB_Checked,AimMode,TAG_DONE);

   GT_SetGadgetAttrs (BlankerGadgets[GID_WASPS],BlankerWindow,NULL,
                      GTSL_Level,NumWasps,TAG_DONE);
   GT_SetGadgetAttrs (BlankerGadgets[GID_BEES],BlankerWindow,NULL,
                      GTSL_Level,NumBees,TAG_DONE);
   GT_SetGadgetAttrs (BlankerGadgets[GID_SPEED],BlankerWindow,NULL,
                      GTCY_Active,Speed-1L,TAG_DONE);
   GT_SetGadgetAttrs (BlankerGadgets[GID_TIGHT],BlankerWindow,NULL,
                      GTSL_Level,Tightness,TAG_DONE);

   GT_SetGadgetAttrs (BlankerGadgets[GID_POPKEY],BlankerWindow,NULL,
                      GTST_String,PopKey,TAG_DONE);
   GT_SetGadgetAttrs (BlankerGadgets[GID_BLANKKEY],BlankerWindow,NULL,
                      GTST_String,BlankKey,TAG_DONE);
  }

 if (DiskObject) FreeDiskObject (DiskObject);
}

void REGARGS SaveConfig(char *Name)

{
 struct DiskObject *DiskObject;
 ULONG Index;
 char **NewToolTypes,**NextEntry,**OldToolTypes;

 if ((DiskObject=GetDiskObjectNew(Name))==NULL)
  {
   DisplayBeep (BlankerWindow->WScreen);
   return;
  }

 Index=0L;
 OldToolTypes=DiskObject->do_ToolTypes;
 while (OldToolTypes[Index]) Index++;
 Index+=NUMTOOLTYPES+1L;

 if ((NewToolTypes=(char **)AllocVec(Index*4L+NUMTOOLTYPES*48L,
                                     MEMF_PUBLIC|MEMF_CLEAR))==NULL)
  {
   FreeDiskObject (DiskObject);
   return;
  }
 NextEntry=NewToolTypes;

 SPrintF (*NextEntry++=(char *)&NewToolTypes[Index],"%s=%ld",ToolTypeIDs[0],
                                                    CxPri);
 SPrintF (*NextEntry++=NewToolTypes[0]+48L,"%s=NO",ToolTypeIDs[1]);
 SPrintF (*NextEntry++=NewToolTypes[1]+48L,"%s=%s",ToolTypeIDs[2],PopKey);
 SPrintF (*NextEntry++=NewToolTypes[2]+48L,"%s=%s",ToolTypeIDs[3],BlankKey);
 SPrintF (*NextEntry++=NewToolTypes[3]+48L,"%s=%ld",ToolTypeIDs[4],TimeOut);
 SPrintF (*NextEntry++=NewToolTypes[4]+48L,"%s=%ld",ToolTypeIDs[5],
                                                    ClientTimeOut);
 SPrintF (*NextEntry++=NewToolTypes[5]+48L,"%s=%s",ToolTypeIDs[6],
                                                    DisplayMode->mn_Name);
 SPrintF (*NextEntry++=NewToolTypes[6]+48L,"%s=%s",ToolTypeIDs[7],
                                                    YesNo(Cycle));
 SPrintF (*NextEntry++=NewToolTypes[7]+48L,"%s=%s",ToolTypeIDs[8],
                                                    YesNo(AimMode));
 SPrintF (*NextEntry++=NewToolTypes[8]+48L,"%s=%ld",ToolTypeIDs[9],NumWasps);
 SPrintF (*NextEntry++=NewToolTypes[9]+48L,"%s=%ld",ToolTypeIDs[10],NumBees);
 SPrintF (*NextEntry++=NewToolTypes[10]+48L,"%s=%ld",ToolTypeIDs[11],Speed);
 SPrintF (*NextEntry++=NewToolTypes[11]+48L,"%s=%ld",ToolTypeIDs[12],Tightness);

 Index=0L;
 while (OldToolTypes[Index])
  {
   char *Ptr,*Asgn;

   if (Ptr=(char *)AllocVec(strlen(OldToolTypes[Index])+1L,0L))
    {
     if (Asgn=strchr(strcpy(Ptr,OldToolTypes[Index]),'=')) *Asgn='\0';
     if (FindToolType(NewToolTypes,Ptr)==NULL) *NextEntry++=OldToolTypes[Index];
     FreeVec ((APTR)Ptr);
    }
   Index++;
  }

 DiskObject->do_ToolTypes=NewToolTypes;
 if (!PutDiskObject(Name,DiskObject)) DisplayBeep (BlankerWindow->WScreen);
 DiskObject->do_ToolTypes=OldToolTypes;

 FreeVec ((APTR)NewToolTypes);
 FreeDiskObject (DiskObject);
}

/*

   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 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 (BlankerContext)
  {
   if (BlankerWindow)
    {
     LeftEdge=BlankerWindow->LeftEdge;
     TopEdge=BlankerWindow->TopEdge;
     ClearMenuStrip (BlankerWindow);
     if (BlankerVisualInfo)
      {
       FreeVisualInfo (BlankerVisualInfo);
       BlankerVisualInfo=NULL;
       if (BlankerMenu)
        {
         FreeMenus (BlankerMenu);
         BlankerMenu=NULL;
        }
      }
     CloseWindow (BlankerWindow);
     BlankerWindow=NULL;
    }
   FreeGadgets (BlankerContext);
   BlankerContext=NULL;
  }
}

void OpenBlankerWindow(void)

{
 struct Gadget *Ptr;
 UWORD Index;

 if (BlankerWindow==NULL)
  {
   struct NewGadget NewGadget;

   BlankerContext=NULL;
   if ((Ptr=CreateContext(&BlankerContext))==NULL) return;

   if ((BlankerWindow=OpenWindowTags(NULL,
                  WA_Left,LeftEdge,
                  WA_Top,TopEdge,
                  WA_IDCMP,IDCMP_CLOSEWINDOW|IDCMP_REFRESHWINDOW|
                  IDCMP_VANILLAKEY|IDCMP_MENUPICK|LISTVIEWIDCMP,
                  WA_Flags,WFLG_DRAGBAR|WFLG_DEPTHGADGET|
                  WFLG_CLOSEGADGET|WFLG_SIMPLE_REFRESH,
                  WA_Title,(ULONG)"ASwarm II V1.2a © 1992 by Illenseer/Scheler",
                  WA_AutoAdjust,TRUE,
                  WA_InnerWidth,496,
                  WA_InnerHeight,161L,TAG_DONE))==NULL)
    {
     CloseBlankerWindow();
     return;
    }

   if ((BlankerVisualInfo=GetVisualInfo(BlankerWindow->WScreen,TAG_DONE))==NULL)
    {
     CloseBlankerWindow();
     return;
    }

   if (!WBStarted)
    {
     NewBlankerMenus[1].nm_Flags|=NM_ITEMDISABLED;
     NewBlankerMenus[2].nm_Flags|=NM_ITEMDISABLED;
    }
   if ((BlankerMenu=CreateMenus(NewBlankerMenus,GTMN_FullMenu,TRUE,TAG_DONE))
                    ==NULL)
    {
     CloseBlankerWindow();
     return;
    }
   if (!LayoutMenus(BlankerMenu,BlankerVisualInfo,TAG_DONE))
    {
     CloseBlankerWindow();
     return;
    }
   else SetMenuStrip (BlankerWindow,BlankerMenu);

   BlankerGadgetTags[0].ti_Data=(ULONG)ModeList;
   BlankerGadgetTags[1].ti_Data=(ULONG)DisplayMode->mn_Index;
   BlankerGadgetTags[4].ti_Data=(ULONG)TimeOut;
   BlankerGadgetTags[7].ti_Data=(ULONG)ClientTimeOut;
   if (SysBase->AttnFlags&AFF_68020)
    if (SysBase->AttnFlags&AFF_68030)
     if (SysBase->AttnFlags&AFF_68040) BlankerGadgetTags[10].ti_Data=
                                          (ULONG)&SpeedNames[3];
     else
      {
       BlankerGadgetTags[10].ti_Data=(ULONG)&SpeedNames[2];
       SpeedNames[6]=NULL;
      }
    else
     {
      BlankerGadgetTags[10].ti_Data=(ULONG)&SpeedNames[1];
      SpeedNames[5]=NULL;
     }
   else
    {
     BlankerGadgetTags[10].ti_Data=(ULONG)&SpeedNames[0];
     SpeedNames[4]=NULL;
    }
   BlankerGadgetTags[11].ti_Data=(ULONG)Speed-1L;
   BlankerGadgetTags[15].ti_Data=(ULONG)NumWasps;
   BlankerGadgetTags[23].ti_Data=(ULONG)NumBees;
   BlankerGadgetTags[31].ti_Data=(ULONG)Tightness;
   BlankerGadgetTags[37].ti_Data=(ULONG)Cycle;
   BlankerGadgetTags[39].ti_Data=(ULONG)AimMode;
   BlankerGadgetTags[41].ti_Data=(ULONG)PopKey;
   BlankerGadgetTags[44].ti_Data=(ULONG)BlankKey;

   for (Index=0L; Index<NUM_GADS; Index++)
    {
     NewGadget=NewBlankerGadgets[Index];
     NewGadget.ng_TopEdge+=BlankerWindow->BorderTop;
     NewGadget.ng_VisualInfo=BlankerVisualInfo;
     Ptr=CreateGadget((ULONG)BlankerGadgetKinds[Index],Ptr,&NewGadget,
                      GT_Underscore,(ULONG)'_',TAG_MORE,
                      &BlankerGadgetTags[BlankerTagsIndex[Index]]);
     if ((BlankerGadgets[Index]=Ptr)==NULL)
      {
       CloseBlankerWindow();
       return;
      }
    }

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

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

void About(void)

{
 ULONG IDCMPFlags;

 IDCMPFlags=0L;
 (void)EasyRequestArgs(NULL,&AboutEasyStruct,&IDCMPFlags,NULL);
}

/*

   Function to handle the Commodity Stuff

*/

void REGARGS HandleCxMsg(CxMsg *CxMsg)

{
 ULONG MsgType,MsgID;

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

 switch (MsgType)
  {
   case CXM_IEVENT: /* PopKey was pressed */
    if (MsgID==EVENT_BLANK) TimeUntilBlank=ThisTimeOut=2L;
    else OpenBlankerWindow();
    break;
   case CXM_COMMAND:
    switch (MsgID)
     {
      case CXCMD_DISABLE:
       /* Message created by 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);
     }
   }
}

CxObj REGARGS *InstallHotKey(CxObj *Filter,char *Describ,LONG Event,
                             char *Default)

{
 if (Filter)
  {
   RemoveCxObj (Filter);
   DeleteCxObj (Filter);
  }

 if (Filter=HotKey(Describ,CxPort,Event))
  if (CxObjError(Filter)==0L)
   {
    AttachCxObj (Broker,Filter);
    return Filter;
   }
  else DeleteCxObj (Filter);

 if (Filter=HotKey(Default,CxPort,Event))
  if (CxObjError(Filter)==0L)
   {
    AttachCxObj (Broker,Filter);
    (void)strcpy(Describ,Default);
    return Filter;
   }
  else DeleteCxObj (Filter);

 (void)strcpy(Describ,"<NONE>");
 return NULL;
}

/*

   Open a Screen with the supplied DisplayID

*/

void SpritesOff(void)

{
 ULONG Index;

 OFF_SPRITE /* switch sprites off */
 for (Index=0L; Index<8L; Index++) custom.spr[Index].ctl=0;
}

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

{
 struct TagItem ScreenTags[2];
 struct Screen *Screen;

 ScreenTags[0].ti_Tag=SA_DisplayID;
 ScreenTags[0].ti_Data=ModeNode->mn_DisplayID;
 ScreenTags[1].ti_Tag=TAG_DONE;
 if (Screen=OpenScreenTagList(&NewBlankerScreen,ScreenTags))
  {
   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)

{
 PutMsg (BlankerClientPort,(struct Message *)BlankerClientMsg);

 (void)SetTaskPri(BlankerClientPort->mp_SigTask,SERVER_PRI+1L);

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

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

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

 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))
  {
   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 Swarms

*/

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

WORD REGARGS Random(WORD Max)

{
 static ULONG Num=0L; /* So the last random-number is still stored ! */
 ULONG Sec,Mic;

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

 Num*=Sec;
 Num+=Mic;

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

 return (WORD)Num%Max;
}

ULONG SwarmSize(LONG NumWaps,LONG NumBees)

{
 return sizeof(struct SwarmStruct)+sizeof(WORD)*((ULONG)NumWaps*9L+
                                   (ULONG)NumBees*9L);
}

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

/* allocate Memory and initialize the Swarm(s) */

{
 LONG Index;
 struct SwarmStruct *SP;
 WORD *Ptr;

 if ((SP=AllocVec(SwarmSize(NumWasps,NumBees),0L))==NULL) return NULL;

 SP->ss_NumWasps=NumWasps;
 SP->ss_NumBees=NumBees;
 SP->ss_Width=SwarmScreen->Width;    /* Will probably be changed in a */
 SP->ss_Height=SwarmScreen->Height;  /* future version of ASwarm */
 SP->ss_BeeAcc=(Tightness*BEEACC/Speed/MAX_SPEED)*(MAX_SPEED/2)+1;

 Ptr=(WORD *)&SP[1];
 for (Index=0L; Index<4L; Index++)
  {
   SP->ss_WX[Index]=Ptr;
   Ptr+=NumWasps;
   SP->ss_WY[Index]=Ptr;
   Ptr+=NumWasps;
   SP->ss_X[Index]=Ptr;
   Ptr+=NumBees;
   SP->ss_Y[Index]=Ptr;
   Ptr+=NumBees;
  }
 SP->ss_NB=Ptr;
 SP->ss_MW=Ptr+NumWasps;

 /* Wasps */
 for (Index=0L; Index<NumWasps; Index++)
  {
   WaspX(1,Index)=WaspX(0,Index)=BORDER+Random(SP->ss_Width-2*BORDER);
   WaspY(1,Index)=WaspY(0,Index)=BORDER+Random(SP->ss_Height-2*BORDER);
   WaXVel(Index)=RAND(WASPACC);
   WaYVel(Index)=RAND(WASPACC);
   SP->ss_NB[Index]=0;
  }

 /* Bees */
 for (Index=0L; Index<SP->ss_NumBees; Index++)
  {
   BeeX(1,Index)=BeeX(0,Index)=BORDER+Random(SP->ss_Width-2*BORDER);
   BeeY(1,Index)=BeeY(0,Index)=BORDER+Random(SP->ss_Height-2*BORDER);
   BXVel(Index)=RAND(SP->ss_BeeAcc);
   BYVel(Index)=RAND(SP->ss_BeeAcc);
   SP->ss_NB[MyWasp(Index)=Index%SP->ss_NumWasps]++;
  }

 return SP;
}

/* move Swarms and redraw it */

void REGARGS DrawSwarms(struct SwarmStruct *SP,LONG AimMode,
                        struct RastPort *WaspRP,struct RastPort *BeeRP)

{
 LONG Index;

 /* Wasps */

 for (Index=0L; Index<SP->ss_NumWasps; Index++)
  {
   WaspX(2,Index)=WaspX(1,Index);
   WaspX(1,Index)=WaspX(0,Index);
   WaspY(2,Index)=WaspY(1,Index);
   WaspY(1,Index)=WaspY(0,Index);

   WaXVel(Index)+=RAND(WASPACC);
   WaYVel(Index)+=RAND(WASPACC);

   if (WaXVel(Index)>WASPVEL) WaXVel(Index)=WASPVEL;
   if (WaXVel(Index)<-WASPVEL) WaXVel(Index)=-WASPVEL;
   if (WaYVel(Index)>WASPVEL) WaYVel(Index)=WASPVEL;
   if (WaYVel(Index)<-WASPVEL) WaYVel(Index)=-WASPVEL;

   WaspX(0,Index)=WaspX(1,Index)+WaXVel(Index);
   WaspY(0,Index)=WaspY(1,Index)+WaYVel(Index);

   /* Bounce check for Wasps */

   if ((WaspX(0,Index)<BORDER)||(WaspX(0,Index)>SP->ss_Width-BORDER-1))
    {
     WaXVel(Index)=-WaXVel(Index);
     if (WaspX(0,Index)<BORDER) WaspX(0,Index)=BORDER;
     else WaspX(0,Index)=SP->ss_Width-BORDER-1;
    }
   if ((WaspY(0,Index)<BORDER)||(WaspY(0,Index)>SP->ss_Height-BORDER-1))
    {
     WaYVel(Index)=-WaYVel(Index);
     if (WaspY(0,Index)<BORDER) WaspY(0,Index)=BORDER;
     else WaspY(0,Index)=SP->ss_Height-BORDER-1;
    }
  }

 /* Bees */

 for (Index=0L; Index<SP->ss_NumBees; Index++)
  {
   WORD DX,DY,ChkIndex;
   LONG Distance,NewDistance;

   BeeX(2,Index)=BeeX(1,Index);
   BeeX(1,Index)=BeeX(0,Index);
   BeeY(2,Index)=BeeY(1,Index);
   BeeY(1,Index)=BeeY(0,Index);

   DX=WaspX(1,MyWasp(Index))-BeeX(1,Index);
   DY=WaspY(1,MyWasp(Index))-BeeY(1,Index);
   Distance=(ABS(DX)+ABS(DY))/2L;
   if (Distance==0L) Distance=1L;

   if (AimMode) /* Look out for the nearest wasp if Aim-Mode is on */
    for (ChkIndex=0; ChkIndex<=SP->ss_NumWasps; ChkIndex++)
     if (ChkIndex!=MyWasp(Index))
      {
       LONG NewDX,NewDY;

       NewDX=WaspX(1,ChkIndex)-BeeX(1,Index);
       NewDY=WaspY(1,ChkIndex)-BeeY(1,Index);
       NewDistance=(ABS(NewDX)+ABS(NewDY))/2L;
       if (NewDistance==0L) NewDistance=1L;
       if (Distance>NewDistance)
        {
         DX=NewDX;
         DY=NewDY;
         Distance=NewDistance;
         SP->ss_NB[MyWasp(Index)]--;
         SP->ss_NB[MyWasp(Index)=ChkIndex]++; /* Mark a nearer Wasp */
        }
      }

   BXVel(Index)+=(DX*SP->ss_BeeAcc)/Distance+RAND(3);
   BYVel(Index)+=(DY*SP->ss_BeeAcc)/Distance+RAND(3);

   if (BXVel(Index)>BEEVEL)  BXVel(Index)=BEEVEL;
   if (BXVel(Index)<-BEEVEL) BXVel(Index)=-BEEVEL;
   if (BYVel(Index)>BEEVEL)  BYVel(Index)=BEEVEL;
   if (BYVel(Index)<-BEEVEL) BYVel(Index)=-BEEVEL;

   BeeX(0,Index)=BeeX(1,Index)+BXVel(Index);
   BeeY(0,Index)=BeeY(1,Index)+BYVel(Index);

  /* Bounce check for Bees */

   if ((BeeX(0,Index)<BORDER)||(BeeX(0,Index)>(SP->ss_Width-BORDER-1)))
    {
     BXVel(Index)=-BXVel(Index);
     BeeX(0,Index)=BeeX(1,Index)+BXVel(Index);
    }
   if ((BeeY(0,Index)<BORDER)||(BeeY(0,Index)>(SP->ss_Height-BORDER-1)))
    {
     BYVel(Index)=-BYVel(Index);
     BeeY(0,Index)=BeeY(1,Index)+BYVel(Index);
    }
  }


 /* Move our insects */

 for(Index=0L; Index<SP->ss_NumWasps; Index++)    /* Wasps */
  {
   SetAPen (WaspRP,0);
   Move (WaspRP,WaspX(2,Index),WaspY(2,Index));
   Draw (WaspRP,WaspX(1,Index),WaspY(1,Index));
   SetAPen (WaspRP,1);
   Draw (WaspRP,WaspX(0,Index),WaspY(0,Index));
  }

 for (Index=0L; Index<SP->ss_NumBees; Index++)   /* Bees  */
  {
   SetAPen (BeeRP,0);
   Move (BeeRP,BeeX(2,Index),BeeY(2,Index));
   Draw (BeeRP,BeeX(1,Index),BeeY(1,Index));
   SetAPen (BeeRP,1);
   Draw (BeeRP,BeeX(0,Index),BeeY(0,Index));
  }
}

/*

   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,Cycle,AimMode,Count,Tightness;
 struct SwarmStruct *Swarms;
 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;
 SwarmScreen=BlankerClientMsg->bcm_Screen;
 Speed=BlankerClientMsg->bcm_Speed;
 Cycle=BlankerClientMsg->bcm_Cycle;
 AimMode=BlankerClientMsg->bcm_AimMode;
 Tightness=BlankerClientMsg->bcm_Tightness;

 /* initialize requested Number of Swarms */

 if ((Swarms=CreateSwarms(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];
   SetDrMd (&RastPorts[Index],JAM1);
   InitBitMap (&BitMaps[Index],1L,SwarmScreen->Width,SwarmScreen->Height);
   BitMaps[Index].Planes[0]=SwarmScreen->BitMap.Planes[Index];
  }

 Color=BEE_COL_NUM-1;
 DColor=Cycle?1:0;
 Count=Speed;

 while ((BlankerClientMsg=(struct BlankerClientMsg *)
                           GetMsg(BlankerClientPort))==NULL)
  {
   /* Color Cycling */

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

   /* Synchronisation */

   WaitTOF();
   if (Count<MAX_SPEED)
    {
     Count++;
     continue;
    }
   Count=Speed;

   /* Move the Swarm(s) */

   DrawSwarms (Swarms,AimMode,&RastPorts[0],&RastPorts[1]);
   Signal (BlankerServerTask,BlankerServerSigMask);
  }
 FreeVec ((APTR)Swarms);

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

 Forbid();
 ReplyMsg ((struct Message *)BlankerClientMsg);
}

/*

   The Main Loop

*/

void main(LONG argc,UBYTE **argv)

{
 CxObj *ObjectList;
 struct IntuiMessage *IntMsg;
 CxMsg *BlankerCxMsg;
 struct Screen *BlankerScreen=NULL;
 struct BlankerClientPort *BlankerClientPort=NULL;
 LONG TimeUntilBlack;

 /* open our Libraries */

 AddTool (GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",37L),
          CloseLibrary,0L);
 AddTool (IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.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 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);

 /* Were we started from Workbench or from CLI ? */

 if (WBStarted=(argc==0L))
  {
   /* Workbench: load Config from DiskObject */
   struct WBStartup *WBenchMsg;

   AddTool (IconBase=OpenLibrary(ICONNAME,37L),CloseLibrary,0L);

   WBenchMsg=(struct WBStartup *)argv;
   LoadConfig (WBenchMsg->sm_NumArgs?WBenchMsg->sm_ArgList->wa_Name:NULL);
  }
 else
  {
   struct RDArgs *RDArgs;
   LONG Array[13];

   (void)memset(Array,'\0',sizeof(Array));
   if (RDArgs=ReadArgs(Template,Array,NULL))
    {
     AddTool (RDArgs,FreeArgs,0L);

     CxPri=RDArgsLong(Array[0],0L,-128L,127L);
     (void)strcpy(PopKey,Array[1]?(char *)Array[1]:DEF_POPKEY);
     CxPopUp=Array[2];
     (void)strcpy(BlankKey,Array[3]?(char *)Array[3]:DEF_BLANKKEY);

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

     TimeOut=RDArgsLong(Array[4],DEF_TIMEOUT,1L,MAX_TIMEOUT);
     ClientTimeOut=RDArgsLong(Array[5],DEF_CLIENT_TIMEOUT,1L,
                   MAX_CLIENT_TIMEOUT);
     if ((DisplayMode=FindMode(ModeList,Array[6]?(char *)Array[6]:""))==NULL)
      DisplayMode=GetDefaultMode(ModeList);


     /* get Parameters for Graphics */

     Cycle=Array[7];
     AimMode=Array[8];

     NumWasps=RDArgsLong(Array[9],DEF_WASPS,1L,MAX_WASPS);
     NumBees=RDArgsLong(Array[10],DEF_BEES,1L,MAX_BEES);
     Speed=RDArgsLong(Array[11],DEF_SPEED,1L,MAX_SPEED);
     Tightness=RDArgsLong(Array[12],DEF_TIGHTNESS,1L,MAX_TIGHTNESS);
    }
   else
    {
     PrintFault (IoErr(),"ASwarm");
     Quit (10);
    }
  }

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

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

 NewBroker.nb_Pri=(BYTE)CxPri;
 NewBroker.nb_Port=CxPort;
 AddTool (Broker=CxBroker(&NewBroker,NULL),DeleteCxObjAll,0L);

 /* install our Hot Keys */

 PopKeyFilter=InstallHotKey(NULL,PopKey,EVENT_OPEN_WINDOW,DEF_POPKEY);
 BlankKeyFilter=InstallHotKey(NULL,BlankKey,EVENT_BLANK,DEF_BLANKKEY);

 /* 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 */

 AddTool ((void *)-1L,CloseBlankerWindow,0L);
 if (CxPopUp) 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_SPEED:
          if (Speed==MAX_SPEED) Speed=1L;
          else Speed++;
          break;
         case GID_WASPS:
          NumWasps=Code;
          break;
         case GID_BEES:
          NumBees=Code;
          break;
         case GID_TIGHT:
          Tightness=Code;
          break;
         case GID_CYCLE:
          Cycle=!Cycle;
          break;
         case GID_AIMMODE:
          AimMode=!AimMode;
          break;
         case GID_POPKEY:
          (void)strcpy(PopKey,((struct StringInfo *)
                               Clicked->SpecialInfo)->Buffer);
          PopKeyFilter=InstallHotKey(PopKeyFilter,PopKey,EVENT_OPEN_WINDOW,
                                     DEF_POPKEY);
          GT_SetGadgetAttrs (Clicked,BlankerWindow,NULL,GTST_String,PopKey,
                             TAG_DONE);
          break;
         case GID_BLANKKEY:
          (void)strcpy(BlankKey,((struct StringInfo *)
                                 Clicked->SpecialInfo)->Buffer);
          BlankKeyFilter=InstallHotKey(BlankKeyFilter,BlankKey,EVENT_BLANK,
                                       DEF_BLANKKEY);
          GT_SetGadgetAttrs (Clicked,BlankerWindow,NULL,GTST_String,BlankKey,
                             TAG_DONE);
        }
       break;
      case IDCMP_MENUPICK:
       Code=IntMsg->Code;
       GT_ReplyIMsg (IntMsg);
       switch (Code)
        {
         struct WBStartup *WBenchMsg;

         case 0xF800:
          WBenchMsg=(struct WBStartup *)argv;
          LoadConfig (WBenchMsg->sm_NumArgs?WBenchMsg->sm_ArgList->wa_Name:
                      NULL);
          PopKeyFilter=InstallHotKey(PopKeyFilter,PopKey,EVENT_OPEN_WINDOW,
                                     DEF_POPKEY);
          BlankKeyFilter=InstallHotKey(BlankKeyFilter,BlankKey,EVENT_BLANK,
                                       DEF_BLANKKEY);
          break;
         case 0xF820:
          WBenchMsg=(struct WBStartup *)argv;
          if (WBenchMsg->sm_NumArgs) SaveConfig(WBenchMsg->sm_ArgList->wa_Name);
          break;
         case 0xF860:
          About();
          break;
         case 0xF8A0:
          CloseBlankerWindow();
          break;
         case 0xF8C0:
          Quit (0);
          break;
         case 0xF801:
          LoadConfig (NULL);
          PopKeyFilter=InstallHotKey(PopKeyFilter,PopKey,EVENT_OPEN_WINDOW,
                                     DEF_POPKEY);
          BlankKeyFilter=InstallHotKey(BlankKeyFilter,BlankKey,EVENT_BLANK,
                                       DEF_BLANKKEY);
        }
       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;
         case 'T':
         case 't':
          ActivateGadget (BlankerGadgets[GID_TIMEOUT],BlankerWindow,NULL);
          break;
         case 'L':
         case 'l':
          ActivateGadget (BlankerGadgets[GID_CLIENT],BlankerWindow,NULL);
          break;
         case 'S':
          if (Speed==1L) Speed=MAX_SPEED;
          else Speed--;
          GT_SetGadgetAttrs (BlankerGadgets[GID_SPEED],BlankerWindow,
                             NULL,GTCY_Active,Speed-1L,TAG_DONE);
          break;
         case 's':
          if (Speed==MAX_SPEED) Speed=1L;
          else Speed++;
          GT_SetGadgetAttrs (BlankerGadgets[GID_SPEED],BlankerWindow,
                             NULL,GTCY_Active,Speed-1L,TAG_DONE);
          break;
         case 'W':
          if (NumWasps>1)
           {
            NumWasps--;
            GT_SetGadgetAttrs (BlankerGadgets[GID_WASPS],BlankerWindow,
                               NULL,GTSL_Level,NumWasps,TAG_DONE);
           }
          break;
         case 'w':
          if (NumWasps<MAX_WASPS)
           {
            NumWasps++;
            GT_SetGadgetAttrs (BlankerGadgets[GID_WASPS],BlankerWindow,
                               NULL,GTSL_Level,NumWasps,TAG_DONE);
           }
          break;
         case 'E':
          if (NumBees>1)
           {
            NumBees--;
            GT_SetGadgetAttrs (BlankerGadgets[GID_BEES],BlankerWindow,
                               NULL,GTSL_Level,NumBees,TAG_DONE);
           }
          break;
         case 'e':
          if (NumBees<MAX_BEES)
           {
            NumBees++;
            GT_SetGadgetAttrs (BlankerGadgets[GID_BEES],BlankerWindow,
                               NULL,GTSL_Level,NumBees,TAG_DONE);
           }
          break;
         case 'I':
          if (Tightness>1)
           {
            Tightness--;
            GT_SetGadgetAttrs (BlankerGadgets[GID_TIGHT],BlankerWindow,
                               NULL,GTSL_Level,Tightness,TAG_DONE);
           }
          break;
         case 'i':
          if (Tightness<MAX_TIGHTNESS)
           {
            Tightness++;
            GT_SetGadgetAttrs (BlankerGadgets[GID_TIGHT],BlankerWindow,
                               NULL,GTSL_Level,Tightness,TAG_DONE);
           }
          break;
         case 'C':
         case 'c':
          Cycle=!Cycle;
          GT_SetGadgetAttrs (BlankerGadgets[GID_CYCLE],BlankerWindow,
                             NULL,GTCB_Checked,Cycle,TAG_DONE);
          break;
         case 'A':
         case 'a':
          AimMode=!AimMode;
          GT_SetGadgetAttrs (BlankerGadgets[GID_AIMMODE],BlankerWindow,
                             NULL,GTCB_Checked,AimMode,TAG_DONE);
          break;
         case 'P':
         case 'p':
          ActivateGadget (BlankerGadgets[GID_POPKEY],BlankerWindow,NULL);
          break;
         case 'K':
         case 'k':
          ActivateGadget (BlankerGadgets[GID_BLANKKEY],BlankerWindow,NULL);
        }
       break;
      default:
       GT_ReplyIMsg (IntMsg);
     }

   /* process Commodity Messages */

   while (BlankerCxMsg=(CxMsg *)GetMsg(CxPort)) HandleCxMsg (BlankerCxMsg);

   /* 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 */
       {
        AddTool (BlankerScreen=CreateScreen(ModeList,DisplayMode),
                 CloseScreen,0L);
        BlankerClientMsg.bcm_Message.mn_Node.ln_Type=NT_MESSAGE;
        BlankerClientMsg.bcm_Message.mn_Node.ln_Pri=0;
        BlankerClientMsg.bcm_Message.mn_Length=sizeof(struct BlankerClientMsg);
        BlankerClientMsg.bcm_Message.mn_ReplyPort=FINDPROCPORT;

        BlankerClientMsg.bcm_Screen=BlankerScreen;
        BlankerClientMsg.bcm_SigMask=1L<<bsp_ClientSig;
        BlankerClientMsg.bcm_Wasps=NumWasps;
        BlankerClientMsg.bcm_Bees=NumBees;
        BlankerClientMsg.bcm_Speed=Speed;
        BlankerClientMsg.bcm_Cycle=Cycle;
        BlankerClientMsg.bcm_AimMode=AimMode;
        BlankerClientMsg.bcm_Tightness=Tightness;
        if (BlankerClientPort=CreateBlankerClient(ASwarmClientProcess,
                                                  &BlankerClientMsg))
         {
          TimeUntilBlack=10L*ClientTimeOut; /* try to start Client */
          AddTool (BlankerClientPort,DeleteBlankerClient,(LONG)&BlankerClientMsg);
         }
       }
     }
    else
     {
      if (IntuitionBase->FirstScreen!=BlankerScreen)
       {
        ScreenToFront (BlankerScreen);
        SpritesOff();
       }
      if (TimeUntilBlack)
       {
        TimeUntilBlack--;
        if (TimeUntilBlack==0L) OFF_DISPLAY
        /* Client Time Out reached, turn entire screen black */
       }
     }
  }
}
