/*

   ASwarm II V2.0 - 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.

   Now released as MUI Application.

   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/cia.h>
#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 <libraries/mui.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/diskfont_protos.h>
#include <clib/alib_protos.h>
#include <clib/commodities_protos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/graphics_protos.h>
#include <clib/icon_protos.h>
#include <clib/intuition_protos.h>
#include <clib/macros.h>
#include <clib/muimaster_protos.h>

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

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

#include <pragmas/muimaster_pragmas.h>
#include <pragmas/commodities_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/exec_sysbase_pragmas.h>
#include <pragmas/graphics_pragmas.h>
#include <pragmas/icon_pragmas.h>
#include <pragmas/intuition_pragmas.h>

#define VOID_GETA4  void __interrupt __saveds
#define REGARGS(rv) rv __regargs
#define CHIP(dt)    dt __chip
#define ASM(rv)     rv __saveds __asm
#define REG(r)      register __ ## r

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

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

void chkabort(void)
{}

#else /* some stuff for Dice */

#define VOID_GETA4  __stkargs __geta4 void
#define REGARGS(rv) __regargs rv
#define CHIP(dt)    __chip dt
#define ASM(rv)     __geta4 rv
#define REG(r)      __ ## r

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

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)) /* Hardwarebangers :-) */
#define ciaa   (*((struct CIA *)0xBFE001L))

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,*MUIMasterBase,*IconBase;

LONG WBStarted;

#define NUMTOOLTYPES 17

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

/*

   MUI stuff

*/

/* Defines for the Guide-Node-entries */
#define GUIDE_FILE         "ASwarm.guide"
#define NODE_MAIN_MAIN     "EDIT"

#define NODE_MAIN_HIDE     "HIDE"
#define NODE_MAIN_BLANK    "BLANK"
#define NODE_MAIN_QUIT     "QUIT"

#define NODE_COMM_MAIN     "COMM"
#define NODE_COMM_TIMEOUT  "TIME"
#define NODE_COMM_POPKEY   "POPKEY"
#define NODE_COMM_CLTIME   "CLIENT"
#define NODE_COMM_BLANKKEY "BLANKKEY"
#define NODE_COMM_FITIME   "FINAL"
#define NODE_COMM_JOYSTICK "JOYSTICK"

#define NODE_SWARM_MAIN  "OPTIONS"
#define NODE_SWARM_SPEED "SPEED"
#define NODE_SWARM_WASPS "WASPS"
#define NODE_SWARM_BEES  "BEES"
#define NODE_SWARM_VELO  "VELO"
#define NODE_SWARM_TIGHT "TIGHT"
#define NODE_SWARM_COLOR "COLOR"
#define NODE_SWARM_AIM   "AIM"

#define NODE_DISPLAY_MAIN     "DISPLAY"
#define NODE_DISPLAY_LIST     "SCREEN"
#define NODE_DISPLAY_MOUSE    "MOUSE"

APTR AP_ASwarm,WI_Config;
APTR ST_Time,ST_Pop,ST_Client,ST_Blank,ST_Final;
APTR CY_Speed,CY_Mouse;
APTR SL_Wasps,SL_Bees,SL_Velo,SL_Tight;
APTR CM_Cycle,CM_Aim,CM_JoyStick;
APTR LV_Modes;

#define ID_OPEN_WINDOW    1L
#define ID_CLOSE_WINDOW   2L
#define ID_BLANK          3L

#define ID_TIMEOUT       10L
#define ID_POPKEY        11L
#define ID_CLIENTTIMEOUT 12L
#define ID_BLANKKEY      13L
#define ID_FINALTIMEOUT  14L
#define ID_JOYSTICK      15L

#define ID_SPEED         20L
#define ID_MOUSE         21L
#define ID_WASPS         22L
#define ID_BEES          23L
#define ID_VELOCITY      24L
#define ID_TIGHTNESS     25L
#define ID_CYCLE         26L
#define ID_AIM           27L

#define ID_MODES         30L

#define ID_LOAD_CONFIG   40L
#define ID_SAVE_CONFIG   41L
#define ID_ABOUT         42L
#define ID_DEFAULTS      43L

char *CE_Speed[5],*CE_SpeedPreDef[] =
 {
  "Slow Motion","Very Slow","Slow",
  "Normal","Fast","Very Fast","Incredible"
 };
char *CE_MouseBlank[] = {"Hardware","Window",NULL};

struct NewMenu NM_Config[] =
 {NM_TITLE,"Project",NULL,0,0L,NULL,
  NM_ITEM,"Load Config","L",0,0L,(APTR)ID_LOAD_CONFIG,
  NM_ITEM,"Save Config","S",0,0L,(APTR)ID_SAVE_CONFIG,
  NM_ITEM,NM_BARLABEL,NULL,0,0L,NULL,
  NM_ITEM,"About ...",NULL,0,0L,(APTR)ID_ABOUT,
  NM_ITEM,NM_BARLABEL,NULL,0,0L,NULL,
  NM_ITEM,"Hide","H",0,0L,(APTR)ID_CLOSE_WINDOW,
  NM_ITEM,"Quit","Q",0,0L,(APTR)MUIV_Application_ReturnID_Quit,
  NM_TITLE,"Edit",NULL,0,0L,NULL,
  NM_ITEM,"Reset To Defaults","D",0,0L,(APTR)ID_DEFAULTS,
  NM_END,NULL,NULL,0,0L,NULL};

/*

   Definitions for our Commodity

*/

CxObj *Broker,*PopKeyFilter,*BlankKeyFilter;
struct MsgPort *CxPort;
LONG CxPri,CxPopUp;


UBYTE PopKey[128],BlankKey[128];
LONG JoyStick;

#define EVENT_OPEN_WINDOW 4711L /* What is that number for, Tron ? */
#define EVENT_BLANK       4712L

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

LONG TimeLeft,InitTimeLeft,TimeOut,ClientTimeOut,FinalTimeOut;

#define MAX_TIMEOUT        3600L
#define MAX_CLIENT_TIMEOUT 60L
#define MAX_FINAL_TIMEOUT  3600L

#define DEF_TIMEOUT        60L
#define DEF_CLIENT_TIMEOUT 5L
#define DEF_FINAL_TIMEOUT  0L

#define SERVER_PRI 5L                 /* Don't change this, saves live */
#define CLIENT_PRI -40L

/*

   Definitions for our Blanker Screen

*/

CHIP(UWORD) EmptyPointer[] = {0,0,0,0}; /* Dummy Pointer when using the
                                           One-Pixel-Window-Trick */

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 MAX_VEL       15L

#define DEF_SPEED     4L
#define DEF_WASPS     2L
#define DEF_BEES      25L
#define DEF_TIGHTNESS 5L
#define DEF_VEL       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,MOUSEBLANKMETHOD/K,VELOCITY/N/K,JOYSTICK/S,FINALTIMEOUT/N/K";

/*

    Definitions for Server/Client Communication

*/

BYTE bsp_TimeOut,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,bcm_Velocity;
  LONG bcm_Cycle,bcm_AimMode,bcm_MouseBlank,bcm_FinalTimeOut;
 } BlankerClientMsg;

/*

   Definitions or Swarm Movement

*/

#define BEEACC  3
#define BEEVELOC 2
#define WASPACC 5
#define WASPVELOC 5
#define BORDER  20
#define WASPVEL (WASPVELOC+Velocity)
#define BEEVEL  (BEEVELOC+Velocity)

#define BEE_PEN  1
#define WASP_PEN 2

#define BEE_COL_NUM 33

UWORD BeeColors[BEE_COL_NUM] = /* Color Cycling Table */
 {0x000F,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)

LONG RandN,RandF,RandI;

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

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

    - Tron -

*/

struct ToolNode
 {
  struct ToolNode *Next;
  void *Tool;
  void (*RemProc)(void *,LONG); /* requires stack arguments !!! */
  LONG Size;
 } *ToolList;

REGARGS(void) RemTool(void *Tool)

{
 struct ToolNode **Ptr,*ToolNode;

 Ptr=&ToolList;
 while (ToolNode=*Ptr)
  if (ToolNode->Tool==Tool)
   {
    *Ptr=ToolNode->Next;

    ToolNode->RemProc(ToolNode->Tool,ToolNode->Size);
    FreeVec (ToolNode);

    return; /* This one was missing in ASwarm II V1.0-V1.1 and in
               ASwarm III :-) */
   }
  else Ptr=&ToolNode->Next;
}

void RemoveAll(void)

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

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

{
 struct ToolNode *Ptr;
 void (*NewRemProc)(void *,LONG);
 static BOOL First=TRUE;

 NewRemProc=(void (*)(void *,LONG))ProcPtr;
 if (NewTool==NULL) exit (10);

 if (First)
  {
   First=FALSE;
   if (atexit(RemoveAll))
    {
     NewRemProc (NewTool,NewSize);
     exit (20);
    }
  }

 if ((Ptr=AllocVec(sizeof(struct ToolNode),0L))==NULL)
  {
   NewRemProc (NewTool,NewSize);
   exit (20);
  }
 Ptr->Next=ToolList;
 Ptr->Tool=NewTool;
 Ptr->RemProc=NewRemProc;
 Ptr->Size=NewSize;
 ToolList=Ptr;
}

/*

   Some useful functions

*/

void MUI_Dispose(APTR Object)

{
 MUI_DisposeObject (Object);
}

void MUI_DisplayBeep(APTR WI_Any)

{
 struct Window *Window;

 Window=NULL;
 get (WI_Any,MUIA_Window_Window,&Window);
 if (Window) DisplayBeep (Window->WScreen);
}

void 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 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 DeleteModeList(struct List *ModeList)

{
 struct ModeNode *ModeNode;

 while (ModeNode=(struct ModeNode *)RemHead(ModeList)) FreeVec (ModeNode);

 FreeVec (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 (ModeList);
    return NULL;
   }

 return ModeList;
}

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

REGARGS(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

*/

REGARGS(char *) YesNo(LONG Flag)

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

REGARGS(char *) 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;
}

REGARGS(LONG) 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);
}

/* should be: REGARGS(LONG) ... but DICE 2.07.54 doesn't like that. */

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

REGARGS(void) 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);
 FinalTimeOut=ToolTypeLong(DiskObject,ToolTypeIDs[16],
                           DEF_FINAL_TIMEOUT,1L,MAX_FINAL_TIMEOUT);

 if ((DisplayMode=FindMode(ModeList,ToolTypeString(DiskObject,ToolTypeIDs[6],
                                                   "")))==NULL)
  DisplayMode=GetDefaultMode(ModeList);

 /* get Parameters for Graphics */

 Speed=ToolTypeLong(DiskObject,ToolTypeIDs[11],DEF_SPEED,1L,MAX_SPEED);
 if (stricmp(ToolTypeString(DiskObject,ToolTypeIDs[13],""),
             CE_MouseBlank[1])) MouseBlank=0L;
 else MouseBlank=1L;

 NumWasps=ToolTypeLong(DiskObject,ToolTypeIDs[9],DEF_WASPS,1L,MAX_WASPS);
 NumBees=ToolTypeLong(DiskObject,ToolTypeIDs[10],DEF_BEES,1L,MAX_BEES);
 Tightness=ToolTypeLong(DiskObject,ToolTypeIDs[12],DEF_TIGHTNESS,1L,
                        MAX_TIGHTNESS);
 Velocity=ToolTypeLong(DiskObject,ToolTypeIDs[14],DEF_VEL,1L,MAX_VEL);

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

 if (WI_Config)
  {
   set (ST_Time,MUIA_String_Integer,TimeOut);
   set (ST_Pop,MUIA_String_Contents,PopKey);
   set (ST_Client,MUIA_String_Integer,ClientTimeOut);
   set (ST_Blank,MUIA_String_Contents,BlankKey);
   set (ST_Final,MUIA_String_Integer,FinalTimeOut);

   set (LV_Modes,MUIA_List_Active,DisplayMode->mn_Index);

   set (CY_Speed,MUIA_Cycle_Active,Speed-1L);
   set (CY_Mouse,MUIA_Cycle_Active,MouseBlank);

   set (SL_Wasps,MUIA_Slider_Level,NumWasps);
   set (SL_Bees,MUIA_Slider_Level,NumBees);
   set (SL_Tight,MUIA_Slider_Level,Tightness);
   set (SL_Velo,MUIA_Slider_Level,Velocity);

   set (CM_Cycle,MUIA_Selected,Cycle);
   set (CM_Aim,MUIA_Selected,AimMode);
   set (CM_JoyStick,MUIA_Selected,JoyStick);
  }

 if (DiskObject) FreeDiskObject (DiskObject);
}

REGARGS(void) SaveConfig(char *Name)

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

 if ((DiskObject=GetDiskObjectNew(Name))==NULL)
  {
   MUI_DisplayBeep (WI_Config);
   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);
 SPrintF (*NextEntry++=NewToolTypes[12]+48L,"%s=%s",ToolTypeIDs[13],
          CE_MouseBlank[MouseBlank]);
 SPrintF (*NextEntry++=NewToolTypes[13]+48L,"%s=%ld",ToolTypeIDs[14],Velocity);
 SPrintF (*NextEntry++=NewToolTypes[14]+48L,"%s=%s",ToolTypeIDs[15],YesNo(JoyStick));
 SPrintF (*NextEntry++=NewToolTypes[15]+48L,"%s=%ld",ToolTypeIDs[16],FinalTimeOut);

 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 (Ptr);
    }
   Index++;
  }

 DiskObject->do_ToolTypes=NewToolTypes;
 if (!PutDiskObject(Name,DiskObject)) MUI_DisplayBeep (WI_Config);
 (void)PutDiskObject(Name,DiskObject);
 DiskObject->do_ToolTypes=OldToolTypes;

 FreeVec (NewToolTypes);
 FreeDiskObject (DiskObject);
}

/*

   Our "InputHandler"

*/

#define BIT(w,b) (((w)>>(b))&1)

VOID_GETA4 BlankerAction(CxMsg *CxMsg,CxObj *CO)

{
 struct InputEvent *IE;

 if (JoyStick)
  if (BIT(custom.joy1dat,1)||(BIT(custom.joy1dat,1)^BIT(custom.joy1dat,0))||
      BIT(custom.joy1dat,9)||(BIT(custom.joy1dat,9)^BIT(custom.joy1dat,8))||
      ((ciaa.ciapra&128)==0))
   {
    Signal (BlankerServerProcess,1L<<bsp_InputSig);
    TimeLeft=InitTimeLeft;
   }

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

/*

   Functions for Handling the Configuration Window

*/

LONG GetNum(APTR ST_Object,LONG *Data,LONG Min,LONG Max)

{
 LONG NewData;

 NewData=*Data;
 get (ST_Object,MUIA_String_Integer,&NewData);
 if ((NewData<Min)||(NewData>Max))
  {
   set (ST_Object,MUIA_String_Integer,*Data);
   return FALSE;
  }
 else
  {
   *Data=NewData;
   return TRUE;
  }
}

ASM(LONG) ModeDispFunc(REG(a0) struct Hook *Hook,
                       REG(a2) char **Array,
                       REG(a1) struct ModeNode *ModeNode)

{
 *Array=ModeNode->mn_Node.ln_Name;
 return 0L;
}

struct Hook HK_Mode = {NULL,NULL,(void *)ModeDispFunc,NULL,NULL};

void CloseConfigWindow(void)

{
 if (WI_Config)
  {
   set (WI_Config,MUIA_Window_Open,FALSE);

   DoMethod (AP_ASwarm,OM_REMMEMBER,WI_Config);
   MUI_DisposeObject (WI_Config);

   WI_Config=NULL;
  }
}

/* New Makros for KeyLabels with Guide-Nodes */

REGARGS(APTR) NodeKeyLabelF(char *Label,char HiChar,char *Node)

{
 return TextObject,
         MUIA_Text_PreParse,"\33r",
         MUIA_Text_Contents,Label,
         MUIA_Weight,0,
         MUIA_InnerLeft,0,
         MUIA_InnerRight,0,
         MUIA_Text_HiChar,HiChar,
         MUIA_HelpNode,Node,
        End;
}

REGARGS(APTR) NodeKeyLabel1F(char *Label,char HiChar,char *Node)

{
 return TextObject,
         MUIA_Text_PreParse,"\33r",
         MUIA_Text_Contents,Label,
         MUIA_Weight,0,
         MUIA_InnerLeft,0,
         MUIA_InnerRight,0,
         MUIA_Text_HiChar,HiChar,
         ButtonFrame,
         MUIA_FramePhantomHoriz,TRUE,
         MUIA_HelpNode,Node,
        End;
}

REGARGS(APTR) NodeKeyLabel2F(char *Label,char HiChar,char *Node)

{
 return TextObject,
         MUIA_Text_PreParse,"\33r",
         MUIA_Text_Contents,Label,
         MUIA_Weight,0,
         MUIA_InnerLeft,0,
         MUIA_InnerRight,0,
         MUIA_Text_HiChar,HiChar,
         StringFrame,
         MUIA_FramePhantomHoriz,TRUE,
         MUIA_HelpNode,Node,
        End;
}

REGARGS(APTR) NodeKeyButtonF(char *Name,char Key,char *Node)

{
 return TextObject,
         ButtonFrame,
         MUIA_Text_Contents,Name,
         MUIA_Text_PreParse,"\33c",
         MUIA_Text_HiChar,Key,
         MUIA_ControlChar,Key,
         MUIA_InputMode,MUIV_InputMode_RelVerify,
         MUIA_Background,MUII_ButtonBack,
         MUIA_HelpNode,Node,
        End;
}

/* should be: REGARGS(APTR) ... but DICE 2.07.54 doesn't like that. */

APTR NodeKeySliderF(LONG Min,LONG Max,LONG Level,char Key,char *Node)

{
 return SliderObject,
         MUIA_Slider_Min,Min,
         MUIA_Slider_Max,Max,
         MUIA_Slider_Level,Level,
         MUIA_ControlChar,Key,
         MUIA_HelpNode,Node,
        End;
}

REGARGS(APTR) NodeKeyCheckMarkF(LONG Selected,char Control,char *Node)

{
 return ImageObject,
         ImageButtonFrame,
         MUIA_InputMode,MUIV_InputMode_Toggle,
         MUIA_Image_Spec,MUII_CheckMark,
         MUIA_Image_FreeVert,TRUE,
         MUIA_Selected,Selected,
         MUIA_Background,MUII_ButtonBack,
         MUIA_ShowSelState,FALSE,
         MUIA_ControlChar,Control,
         MUIA_HelpNode,Node,
        End;
}

void OpenConfigWindow(void)

{
 ULONG Index;
 APTR BT_Hide,BT_Blank,BT_Quit;

 if (WI_Config)
  {
   DoMethod (WI_Config,MUIM_Window_ToFront);
   set (WI_Config,MUIA_Window_Open,TRUE);

   return;
  }

 if (SysBase->AttnFlags&AFF_68020)
  if (SysBase->AttnFlags&AFF_68030)
   if (SysBase->AttnFlags&AFF_68040) Index=3L;
   else Index=2L;
  else Index=1L;
 else Index=0L;
 (void)memcpy(&CE_Speed[0],&CE_SpeedPreDef[Index],4L*sizeof(char *));
 CE_Speed[4]=NULL;

 if (!WBStarted)
  {
   NM_Config[1].nm_Flags|=NM_ITEMDISABLED;
   NM_Config[2].nm_Flags|=NM_ITEMDISABLED;
  }

 if (WI_Config=WindowObject,
                MUIA_Window_Title,"ASwarm II V2.0",
                MUIA_Window_ID,"CONFIG",
                MUIA_Window_Menu,NM_Config,
                MUIA_HelpNode, NODE_MAIN_MAIN,
                WindowContents,VGroup,
                 Child,ColGroup(4),GroupFrameT("Commodity Options"),
                  MUIA_HelpNode,NODE_COMM_MAIN,
                  Child,NodeKeyLabel2F("Timeout",'t',NODE_COMM_TIMEOUT),
                  Child,ST_Time=StringObject,
                   StringFrame,
                   MUIA_ControlChar,'t',
                   MUIA_String_Integer,TimeOut,
                   MUIA_String_Accept,"0123456789",
                   MUIA_HorizWeight,25,
                   MUIA_HelpNode,NODE_COMM_TIMEOUT,
                  End,

                  Child,NodeKeyLabel2F("Pop Key",'p',NODE_COMM_POPKEY),
                  Child,ST_Pop=StringObject,
                   StringFrame,
                   MUIA_String_Contents,PopKey,
                   MUIA_String_MaxLen,128L,
                   MUIA_ControlChar,'p',
                   MUIA_HelpNode,NODE_COMM_POPKEY,
                  End,

                  Child,NodeKeyLabel2F("Client Timeout",'l',NODE_COMM_CLTIME),
                  Child,ST_Client=StringObject,
                   StringFrame,
                   MUIA_ControlChar,'l',
                   MUIA_String_Integer,ClientTimeOut,
                   MUIA_String_Accept,"0123456789",
                   MUIA_HorizWeight,25,
                   MUIA_HelpNode,NODE_COMM_CLTIME,
                  End,

                  Child,NodeKeyLabel2F("Blank Key",'k',NODE_COMM_BLANKKEY),
                  Child,ST_Blank=StringObject,
                   StringFrame,
                   MUIA_String_Contents,BlankKey,
                   MUIA_String_MaxLen,128L,
                   MUIA_ControlChar,'k',
                   MUIA_HelpNode,NODE_COMM_BLANKKEY,
                  End,

                  Child,NodeKeyLabel2F("Final Timeout",'f',NODE_COMM_FITIME),
                  Child,ST_Final=StringObject,
                   StringFrame,
                   MUIA_ControlChar,'f',
                   MUIA_String_Integer,FinalTimeOut,
                   MUIA_String_Accept,"0123456789",
                   MUIA_HorizWeight,25,
                   MUIA_HelpNode,NODE_COMM_FITIME,
                  End,

                  Child,NodeKeyLabel1F("JoyStick",'j',NODE_COMM_JOYSTICK),
                  Child,HGroup,
                   Child,CM_JoyStick=NodeKeyCheckMarkF(JoyStick,'j',NODE_COMM_JOYSTICK),
                   Child,RectangleObject,End,
                   MUIA_HelpNode,NODE_COMM_JOYSTICK,
                  End,
                 End,
                 Child,HGroup,
                  Child,VGroup,GroupFrameT("Swarm Options"),
                  MUIA_HelpNode, NODE_SWARM_MAIN,
                   Child,ColGroup(2),
                    Child,NodeKeyLabel1F("Speed",'s',NODE_SWARM_SPEED),
                    Child,CY_Speed=CycleObject,
                     MUIA_Cycle_Active,Speed-1L,
                     MUIA_Cycle_Entries,CE_Speed,
                     MUIA_ControlChar,'s',
                     MUIA_HelpNode, NODE_SWARM_SPEED,
                    End,

                    Child,NodeKeyLabelF("Wasps",'w',NODE_SWARM_WASPS),
                    Child,SL_Wasps=NodeKeySliderF(1L,MAX_WASPS,NumWasps,'w',NODE_SWARM_WASPS),

                    Child,NodeKeyLabelF("Bees",'e',NODE_SWARM_BEES),
                    Child,SL_Bees=NodeKeySliderF(1L,MAX_BEES,NumBees,'e',NODE_SWARM_BEES),

                    Child,NodeKeyLabelF("Velocity",'v',NODE_SWARM_VELO),
                    Child,SL_Velo=NodeKeySliderF(1L,MAX_VEL,Velocity,'v',NODE_SWARM_VELO),

                    Child,NodeKeyLabelF("Tightness",'i',NODE_SWARM_TIGHT),
                    Child,SL_Tight=NodeKeySliderF(1L,MAX_TIGHTNESS,Tightness,'i',NODE_SWARM_TIGHT),

                    Child,NodeKeyLabel1F("Color Cycling",'c',NODE_SWARM_COLOR),
                    Child,CM_Cycle=NodeKeyCheckMarkF(Cycle,'c',NODE_SWARM_COLOR),

                    Child,NodeKeyLabel1F("Aim",'a',NODE_SWARM_AIM),
                    Child,CM_Aim=NodeKeyCheckMarkF(AimMode,'a',NODE_SWARM_AIM),
                   End,
                   Child,RectangleObject,End,
                  End,
                  Child,VGroup,GroupFrameT("Display Options"),MUIA_HelpNode,NODE_DISPLAY_MAIN,
                   Child,LV_Modes=ListviewObject,
                    MUIA_Listview_List,ListObject,
                     MUIA_List_DisplayHook,&HK_Mode,
                     MUIA_List_AdjustWidth,TRUE,
                     MUIA_HelpNode,NODE_DISPLAY_LIST,
                     InputListFrame,
                    End,
                   End,

                   Child,HGroup,
                    Child,NodeKeyLabel1F("Mouse Blank",'m',NODE_DISPLAY_MOUSE),
                    Child,CY_Mouse=CycleObject,
                     MUIA_Cycle_Active,MouseBlank,
                     MUIA_Cycle_Entries,CE_MouseBlank,
                     MUIA_ControlChar,'m',
                     MUIA_HelpNode, NODE_DISPLAY_MOUSE,
                    End,

                   End,
                  End,
                 End,
                 Child,HGroup,
                  Child,BT_Hide=NodeKeyButtonF("Hide",'h',NODE_MAIN_HIDE),
                  Child,BT_Blank=NodeKeyButtonF("Blank",'b',NODE_MAIN_BLANK),
                  Child,BT_Quit=NodeKeyButtonF("Quit",'q',NODE_MAIN_QUIT),
                 End,
                End,
               End)

  /* Have you seen this? No f*cking macros for GadTools needed to calculate
     the size and position of gadgets */

  {
   struct ModeNode *ModeNode;

   DoMethod (AP_ASwarm,OM_ADDMEMBER,WI_Config);

   DoMethod (WI_Config,MUIM_Notify,MUIA_Window_CloseRequest,TRUE,
             AP_ASwarm,2,MUIM_Application_ReturnID,ID_CLOSE_WINDOW);
   DoMethod (ST_Time,MUIM_Notify,MUIA_String_Acknowledge,MUIV_EveryTime,
             AP_ASwarm,2,MUIM_Application_ReturnID,ID_TIMEOUT);
   DoMethod (ST_Pop,MUIM_Notify,MUIA_String_Acknowledge,MUIV_EveryTime,
             AP_ASwarm,2,MUIM_Application_ReturnID,ID_POPKEY);
   DoMethod (ST_Client,MUIM_Notify,MUIA_String_Acknowledge,MUIV_EveryTime,
             AP_ASwarm,2,MUIM_Application_ReturnID,ID_CLIENTTIMEOUT);
   DoMethod (ST_Blank,MUIM_Notify,MUIA_String_Acknowledge,MUIV_EveryTime,
             AP_ASwarm,2,MUIM_Application_ReturnID,ID_BLANKKEY);
   DoMethod (ST_Final,MUIM_Notify,MUIA_String_Acknowledge,MUIV_EveryTime,
             AP_ASwarm,2,MUIM_Application_ReturnID,ID_FINALTIMEOUT);
   DoMethod (CM_JoyStick,MUIM_Notify,MUIA_Selected,MUIV_EveryTime,
             AP_ASwarm,2,MUIM_Application_ReturnID,ID_JOYSTICK);
   DoMethod (CY_Speed,MUIM_Notify,MUIA_Cycle_Active,MUIV_EveryTime,
             AP_ASwarm,2,MUIM_Application_ReturnID,ID_SPEED);
   DoMethod (SL_Wasps,MUIM_Notify,MUIA_Slider_Level,MUIV_EveryTime,
             AP_ASwarm,2,MUIM_Application_ReturnID,ID_WASPS);
   DoMethod (SL_Bees,MUIM_Notify,MUIA_Slider_Level,MUIV_EveryTime,
             AP_ASwarm,2,MUIM_Application_ReturnID,ID_BEES);
   DoMethod (SL_Velo,MUIM_Notify,MUIA_Slider_Level,MUIV_EveryTime,
             AP_ASwarm,2,MUIM_Application_ReturnID,ID_VELOCITY);
   DoMethod (SL_Tight,MUIM_Notify,MUIA_Slider_Level,MUIV_EveryTime,
             AP_ASwarm,2,MUIM_Application_ReturnID,ID_TIGHTNESS);
   DoMethod (CM_Cycle,MUIM_Notify,MUIA_Selected,MUIV_EveryTime,
             AP_ASwarm,2,MUIM_Application_ReturnID,ID_CYCLE);
   DoMethod (CM_Aim,MUIM_Notify,MUIA_Selected,MUIV_EveryTime,
             AP_ASwarm,2,MUIM_Application_ReturnID,ID_AIM);
   DoMethod (LV_Modes,MUIM_Notify,MUIA_List_Active,MUIV_EveryTime,
             AP_ASwarm,2,MUIM_Application_ReturnID,ID_MODES);
   DoMethod (CY_Mouse,MUIM_Notify,MUIA_Cycle_Active,MUIV_EveryTime,
             AP_ASwarm,2,MUIM_Application_ReturnID,ID_MOUSE);
   DoMethod (BT_Hide,MUIM_Notify,MUIA_Pressed,FALSE,
             AP_ASwarm,2,MUIM_Application_ReturnID,ID_CLOSE_WINDOW);
   DoMethod (BT_Blank,MUIM_Notify,MUIA_Pressed,FALSE,
             AP_ASwarm,2,MUIM_Application_ReturnID,ID_BLANK);
   DoMethod (BT_Quit,MUIM_Notify,MUIA_Pressed,FALSE,
             AP_ASwarm,2,MUIM_Application_ReturnID,MUIV_Application_ReturnID_Quit);

   DoMethod (WI_Config,MUIM_Window_SetCycleChain,
             ST_Time,ST_Pop,ST_Client,ST_Blank,ST_Final,CM_JoyStick,
             CY_Speed,SL_Wasps,SL_Bees,SL_Velo,SL_Tight,CM_Cycle,CM_Aim,
             LV_Modes,CY_Mouse,
             BT_Hide,BT_Blank,BT_Quit,NULL);

   ModeNode=(struct ModeNode *)ModeList->lh_Head;
   while (ModeNode->mn_Node.ln_Succ)
    {
     DoMethod (LV_Modes,MUIM_List_Insert,&ModeNode,1L,MUIV_List_Insert_Bottom);
     ModeNode=(struct ModeNode *)ModeNode->mn_Node.ln_Succ;
    }

   set (WI_Config,MUIA_Window_Open,TRUE);

   set (LV_Modes,MUIA_List_Active,DisplayMode->mn_Index);
  }

 DoMethod (WI_Config,MUIM_Window_ScreenToFront);
 DoMethod (WI_Config,MUIM_Window_ToFront);

 set (WI_Config,MUIA_Window_Open,TRUE);

}

void About(void)

{
 (void)MUI_Request(AP_ASwarm,WI_Config,0L, "About","*Ok",
                   "\33cASwarm II V2.0 - The Original One\n"
                   "(c) 1992-1994 by Markus Illenseer and Matthias Scheler\n"
                   "\33cThis program is freely distributable.\n\n"
                   "\33cThis is a MUI Application.",TAG_END);
}

/*

   Function to handle the Commodity Stuff

*/

ASM(void) CxDispatcher(REG(a0) struct Hook *Hook,
                       REG(a2) APTR AP_Object,
                       REG(a1) CxMsg *CxMsg)

{
 ULONG MsgType,MsgID;

 MsgType=CxMsgType(CxMsg);
 MsgID=CxMsgID(CxMsg);

 switch (MsgType)
  {
   case CXM_IEVENT:
    if (MsgID==EVENT_BLANK) DoMethod (AP_Object,MUIM_Application_ReturnID,ID_BLANK);
    else DoMethod (AP_Object,MUIM_Application_ReturnID,ID_OPEN_WINDOW);
    break;
   case CXM_COMMAND:
    switch (MsgID)
     {
      case CXCMD_UNIQUE:
       DoMethod (AP_Object,MUIM_Application_ReturnID,ID_OPEN_WINDOW);
     }
  }
}

struct Hook HK_Broker = {NULL,NULL,(void *)CxDispatcher,NULL,NULL};

REGARGS(CxObj *) 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;
}

/*

   Hooks for ARexx Port

*/

ASM(LONG) RxEnable(REG(a0) struct Hook *Hook,
                   REG(a2) APTR AP_Object,
                   REG(a1) LONG *Array)

{
 (void)ActivateCxObj(Broker,TRUE);

 return 0L;
}

struct Hook HK_RxEnable = {NULL,NULL,(void *)RxEnable,NULL,NULL};

ASM(LONG) RxDisable(REG(a0) struct Hook *Hook,
                    REG(a2) APTR AP_Object,
                    REG(a1) LONG *Array)

{
 (void)ActivateCxObj(Broker,FALSE);
 Signal (BlankerServerProcess,1L<<bsp_InputSig);

 return 0L;
}

struct Hook HK_RxDisable = {NULL,NULL,(void *)RxDisable,NULL,NULL};

struct MUI_Command ASwarmRexxCommands[] =
 {
  {"openwindow",MC_TEMPLATE_ID,ID_OPEN_WINDOW,NULL},
  {"closewindow",MC_TEMPLATE_ID,ID_CLOSE_WINDOW,NULL},
  {"enable",NULL,0L,&HK_RxEnable},
  {"disable",NULL,0L,&HK_RxDisable},
  {"blank",MC_TEMPLATE_ID,ID_BLANK,NULL},
  {NULL,NULL,NULL,NULL}
 };


/*

   Functions fore Creating/Deleting the Client Process

*/

void DeleteBlankerClient(struct MsgPort *BlankerClientPort,
                         struct BlankerClientMsg *BlankerClientMsg)

{
 Forbid();
 PutMsg (BlankerClientPort,(struct Message *)BlankerClientMsg);
 (void)SetTaskPri(BlankerClientPort->mp_SigTask,SERVER_PRI+1L);
 Permit();

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

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

{
 struct Process *BlankerClientProcess;

 if (BlankerClientProcess=CreateNewProcTags(NP_Entry,CodePtr,
                                            NP_Name,"BlankerClient",
                                            NP_StackSize,4000L,TAG_DONE))
  {
   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;
}

/*

   Open a Screen with the supplied DisplayID

*/

REGARGS(void) SpritesOff(struct Screen *BlankerScreen)

{
 ULONG Index;

 if (!MouseBlank)
  {
   OFF_SPRITE /* switch sprites off */
   for (Index=0L; Index<8L; Index++) custom.spr[Index].ctl=0;
  }
 else /* reactivate our window to show our null mouse pointer */
  if (BlankerScreen->FirstWindow) ActivateWindow (BlankerScreen->FirstWindow);
}

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

{
 struct Screen *Screen;

 if (Screen=OpenScreenTags(NULL,
                           SA_Depth,2,
                           SA_Title,"ASwarm II",
                           SA_DisplayID,ModeNode->mn_DisplayID,
                           SA_Quiet,TRUE,TAG_DONE))
  {
   SetRGB4 (&Screen->ViewPort,0,0,0,0);
   SetRast (&Screen->RastPort,0);

   SpritesOff (Screen);
  }
 return Screen;
}

/*

   Functions for Creating/Drawing/Removing the Swarms

*/

void InitRandom(void)

{
 ULONG Time[2];

 CurrentTime (&Time[0],&Time[1]);
 RandN=(LONG)Time[0];
 if (Time[1]<1000L)
  {
   RandF=4*Time[1]+1;
   RandI=2*Time[1]+1;
  }
 else
  {
   RandF=4*(Time[1]/1000L)+1;
   RandI=2*(Time[1]/1000L)+1;
  }
}

REGARGS(WORD) Random(WORD Max)

{
 RandN=RandF*RandN+RandI;
 if (RandN<0L) RandN=-RandN;

 return (WORD)(RandN%Max);
}

BYTE SquareRootTab[256] = /* The mighty 256 Bytes eater :-) */
 {0,1,1,1,2,2,2,2,2,3,3,3,3,3,3,3,
  4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,
  5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,
  6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
  8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
  8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
  9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,
  10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,
  11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
  12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,
  12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
  13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,
  14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
  14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
  15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15};


REGARGS(LONG) FastSQRT(LONG x) /* *Grin* Nice idea, Tron */

{
 LONG sr;

 sr=1L;
 while (x>255L)
  {
   x/=4L;
   sr*=2L;
  }
 return sr*(LONG)SquareRootTab[x];
}

REGARGS(ULONG) SwarmSize(LONG NumWaps,LONG NumBees)

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

/* should be: REGARGS(...) ... but DICE 2.07.54 doesn't like that. */

struct SwarmStruct *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)/MAX_SPEED+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 */

REGARGS(void) DrawSwarms(struct RastPort *RP,struct SwarmStruct *SP,
                         LONG AimMode,LONG Velocity)

{
 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;
*/
     WaspX(0,Index)+=WaXVel(Index);
    }
   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;
*/
     WaspY(0,Index)+=WaYVel(Index);
    }
  }

 /* 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=FastSQRT(DX*DX+DY*DY);
   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=FastSQRT(NewDX*NewDX+NewDY*NewDY);
       if (Distance>NewDistance)
        {
         DX=NewDX;
         DY=NewDY;
         if (NewDistance==0L) Distance=1L;
         else 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);
*/
     BeeX(0,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);
*/
     BeeY(0,Index)+=BYVel(Index);
    }
  }


 /* Move our insects */

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

   if(WaspX(0,Index)<0 || WaspX(0,Index)>SP->ss_Width ||
      WaspY(0,Index)<0 || WaspY(0,Index)>SP->ss_Height) goto DoNotDraw;


   SetAPen (RP,WASP_PEN);
   Draw (RP,WaspX(0,Index),WaspY(0,Index));

DoNotDraw:
   ;
  }

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

   if(BeeX(0,Index)<0 || BeeX(0,Index)>SP->ss_Width || 
      BeeY(0,Index)<0 || BeeY(0,Index)>SP->ss_Height) goto DoNotDraw2;

   SetAPen (RP,BEE_PEN);
   Draw (RP,BeeX(0,Index),BeeY(0,Index));

DoNotDraw2:
   ;

  }
}

/*

   This is the Client Process's Main Loop

*/

VOID_GETA4 ASwarmClientProcess(void)

{
 struct BlankerClientMsg *BlankerClientMsg;
 struct MsgPort *BlankerClientPort;
 struct Task *BlankerServerTask;
 ULONG BlankerServerSigMask,Seconds,Micros,EndTime;
 struct Screen *SwarmScreen;
 LONG NumWasps,NumBees,Speed,Cycle,AimMode,Count,Tightness;
 struct SwarmStruct *Swarms;
 WORD Color,DColor;

 /* 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;
 Velocity=BlankerClientMsg->bcm_Velocity;

 if (BlankerClientMsg->bcm_FinalTimeOut)
  {
   CurrentTime (&Seconds,&Micros);
   EndTime=Seconds+BlankerClientMsg->bcm_FinalTimeOut;
  }
 else EndTime=0L;

 /* init pseudo random number generator */

 InitRandom();

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

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

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

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

   /* Synchronisation */

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

   /* Move the Swarm(s) */

   DrawSwarms (&SwarmScreen->RastPort,Swarms,AimMode,Velocity);
   if (IntuitionBase->FirstScreen!=SwarmScreen)
    {
     ScreenToFront (SwarmScreen);
     SpritesOff (SwarmScreen);
    }
   Signal (BlankerServerTask,BlankerServerSigMask);

   if (EndTime)
    {
     CurrentTime (&Seconds,&Micros);
     if (Seconds>=EndTime) /* final timeout reached, turn screen black */
      {
       SetRGB4 (&SwarmScreen->ViewPort,BEE_PEN,0,0,0);
       SetRGB4 (&SwarmScreen->ViewPort,WASP_PEN,0,0,0);
       (void)WaitPort(BlankerClientPort);
      }
    }
  }
 FreeVec (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 Screen *BlankerScreen=NULL;
 struct Window *BlankerWindow=NULL;
 struct MsgPort *BlankerClientPort=NULL;
 struct DiskObject *DiskObject;

 /* 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 (IconBase=OpenLibrary(ICONNAME,37L),CloseLibrary,0L);
 AddTool (MUIMasterBase=OpenLibrary(MUIMASTER_NAME,MUIMASTER_VMIN),CloseLibrary,0L);

 /* create our MUI application object */

 if (DiskObject=GetDiskObject("PROGDIR:ASwarm")) AddTool (DiskObject,FreeDiskObject,0L);

 AP_ASwarm=ApplicationObject,
            MUIA_Application_Title,"ASwarm II",
            MUIA_Application_Version,Version,
            MUIA_Application_Copyright,
             "Copyright © 1992-1994, Markus Illenseer & Matthias Scheler",
            MUIA_Application_Author,"Markus Illenseer & Matthias Scheler",
            MUIA_Application_Description,"Screen Blanker based on XSwarm",
            MUIA_Application_Base,"ASwarm",
            MUIA_Application_SingleTask,TRUE,
            MUIA_Application_DiskObject,DiskObject,
            MUIA_Application_BrokerHook,&HK_Broker,
            MUIA_Application_BrokerPri,CxPri,
            MUIA_Application_Commands,ASwarmRexxCommands,
            MUIA_HelpFile,GUIDE_FILE,
           End;

 if (AP_ASwarm==NULL) exit (10L);
 AddTool (AP_ASwarm,MUI_Dispose,0L);

 /* create List of Graphics Modes */

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

 /* get some Signals */

 BlankerServerProcess=SysBase->ThisTask;
 if ((bsp_TimeOut=AllocSignal(-1L))==-1) exit (10);
 AddTool ((void *)bsp_TimeOut,FreeSignal,0L);
 if ((bsp_InputSig=AllocSignal(-1L))==-1) exit (10);
 AddTool ((void *)bsp_InputSig,FreeSignal,0L);
 if ((bsp_ClientSig=AllocSignal(-1L))==-1) exit (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;

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

   (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];
     JoyStick=Array[15];

     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);
     if (stricmp(Array[13]?(char *)Array[13]:"",CE_MouseBlank[1]))
      MouseBlank=0L;
     else MouseBlank=1L;
     Velocity=RDArgsLong(Array[14],DEF_VEL,1L,MAX_VEL);
     FinalTimeOut=RDArgsLong(Array[16],DEF_FINAL_TIMEOUT,0L,MAX_FINAL_TIMEOUT);
    }
   else
    {
     PrintFault (IoErr(),"ASwarm");
     exit (10);
    }
  }

 /* get our Broker from MUI */

 Broker=NULL;
 get (AP_ASwarm,MUIA_Application_Broker,&Broker);
 if (Broker==NULL) exit (10);

 CxPort=NULL;
 get (AP_ASwarm,MUIA_Application_BrokerPort,&CxPort);
 if (CxPort==NULL) exit (10);

 /* install our Hot Keys */

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

 /* install our "InputHandler" */

 TimeLeft=InitTimeLeft=TimeOut*10L;
 ObjectList=CxCustom(BlankerAction,0L);
 AttachCxObj (Broker,ObjectList);
 if (CxObjError(ObjectList)) exit (10);

 (void)ActivateCxObj(Broker,TRUE);

 /* open Window on startup if not forbidden */

 if (CxPopUp) OpenConfigWindow();

 /* increase our Priority */

 AddTool (BlankerServerProcess,SetTaskPri,
          (LONG)SetTaskPri(BlankerServerProcess,SERVER_PRI));

 /* start the Loop */

 FOREVER
  {
   ULONG Signal,Mask;

   /* handle MUI events */

   do
    {
     char *Ptr;
     struct WBStartup *WBenchMsg;

     switch (DoMethod(AP_ASwarm,MUIM_Application_Input,&Signal))
      {
       case MUIV_Application_ReturnID_Quit:
        exit (0);
       case ID_OPEN_WINDOW:
        OpenConfigWindow();
        break;
       case ID_CLOSE_WINDOW:
        CloseConfigWindow();
        break;
       case ID_BLANK:
        TimeLeft=InitTimeLeft=2L;
        break;

       case ID_TIMEOUT:
        if (GetNum(ST_Time,&TimeOut,1L,MAX_TIMEOUT)) TimeLeft=InitTimeLeft=10L*TimeOut;
        break;
       case ID_POPKEY:
        Ptr=NULL;
        get (ST_Pop,MUIA_String_Contents,&Ptr);
        if (Ptr)
         {
          (void)strcpy(PopKey,Ptr);
          PopKeyFilter=InstallHotKey(PopKeyFilter,PopKey,
                                     EVENT_OPEN_WINDOW,DEF_POPKEY);
          set (ST_Pop,MUIA_String_Contents,PopKey);
         }
        break;
       case ID_CLIENTTIMEOUT:
        (void)GetNum(ST_Client,&ClientTimeOut,1L,MAX_CLIENT_TIMEOUT);
        break;
       case ID_BLANKKEY:
        Ptr=NULL;
        get (ST_Blank,MUIA_String_Contents,&Ptr);
        if (Ptr)
         {
          (void)strcpy(BlankKey,Ptr);
          BlankKeyFilter=InstallHotKey(BlankKeyFilter,BlankKey,
                                       EVENT_BLANK,DEF_BLANKKEY);
          set (ST_Blank,MUIA_String_Contents,BlankKey);
         }
        break;
       case ID_FINALTIMEOUT:
        (void)GetNum(ST_Final,&FinalTimeOut,0L,MAX_FINAL_TIMEOUT);
        break;
       case ID_JOYSTICK:
        get (CM_JoyStick,MUIA_Selected,&JoyStick);
        break;

       case ID_SPEED:
        Speed--;
        get (CY_Speed,MUIA_Cycle_Active,&Speed);
        Speed++;
        break;        
       case ID_MOUSE:
        get (CY_Mouse,MUIA_Cycle_Active,&MouseBlank);
        break;        
       case ID_WASPS:
        get (SL_Wasps,MUIA_Slider_Level,&NumWasps);
        break;        
       case ID_BEES:
        get (SL_Bees,MUIA_Slider_Level,&NumBees);
        break;        
       case ID_VELOCITY:
        get (SL_Velo,MUIA_Slider_Level,&Velocity);
        break;        
       case ID_TIGHTNESS:
        get (SL_Tight,MUIA_Slider_Level,&Tightness);
        break;        
       case ID_CYCLE:
        get (CM_Cycle,MUIA_Selected,&Cycle);
        break;
       case ID_AIM:
        get (CM_Aim,MUIA_Selected,&AimMode);
        break;

       case ID_MODES:
        {
         struct ModeNode *NewMode;

         NewMode=NULL;
         DoMethod (LV_Modes,MUIM_List_GetEntry,MUIV_List_GetEntry_Active,&NewMode);
         if (NewMode) DisplayMode=NewMode;
         break;
        }

       case ID_LOAD_CONFIG:
        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 ID_SAVE_CONFIG:
        WBenchMsg=(struct WBStartup *)argv;
        if (WBenchMsg->sm_NumArgs) SaveConfig(WBenchMsg->sm_ArgList->wa_Name);
        break;
       case ID_ABOUT:
        About();
        break;
       case ID_DEFAULTS:
        LoadConfig (NULL);
        PopKeyFilter=InstallHotKey(PopKeyFilter,PopKey,EVENT_OPEN_WINDOW,
                                   DEF_POPKEY);
        BlankKeyFilter=InstallHotKey(BlankKeyFilter,BlankKey,EVENT_BLANK,
                                     DEF_BLANKKEY);
      }
    }
   while (Signal==0L);

   /* wait */

   Mask=Wait(MASK(bsp_TimeOut)|MASK(bsp_InputSig)|MASK(bsp_ClientSig)|SIGBREAKF_CTRL_C|Signal);

   /* check for <CTRL>-C */

   if (Mask&SIGBREAKF_CTRL_C) exit (0);

   /* Input detected, unblank if necessary */

   if (Mask&MASK(bsp_InputSig))
    {
     if (BlankerScreen)
      {
       if (BlankerClientPort) RemTool (BlankerClientPort);
       if (BlankerWindow) RemTool (BlankerWindow);
       RemTool (BlankerScreen);
       BlankerScreen=NULL;
       InitTimeLeft=TimeOut*10L;
       ON_SPRITE
      }
     TimeLeft=InitTimeLeft;
    }

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

   if (Mask&MASK(bsp_ClientSig)) TimeLeft=InitTimeLeft;

   /* time run out */

   if (TimeLeft==0L)
    if (BlankerScreen)
     {
      /* Client Time Out reached, turn entire screen black */
      SetRGB4 (&BlankerScreen->ViewPort,BEE_PEN,0,0,0);
      SetRGB4 (&BlankerScreen->ViewPort,WASP_PEN,0,0,0);

      /* check again after one second to keep screen in front */
      TimeLeft=10L;
      if (IntuitionBase->FirstScreen!=BlankerScreen) ScreenToFront (BlankerScreen);
      SpritesOff (BlankerScreen);
     }
    else
     {
      AddTool (BlankerScreen=CreateScreen(ModeList,DisplayMode),
               CloseScreen,0L);
      if (MouseBlank)
       if (BlankerWindow=OpenWindowTags(NULL,
                                        WA_Width,1,
                                        WA_Height,1,
                                        WA_CustomScreen,BlankerScreen,
                                        WA_Flags,WFLG_ACTIVATE|WFLG_RMBTRAP|
                                         WFLG_BORDERLESS|WFLG_SIMPLE_REFRESH,
                                        TAG_DONE))
        {
         AddTool (BlankerWindow,CloseWindow,0L);
         SetPointer (BlankerWindow,EmptyPointer,1L,1L,0L,0L);
        }
       else
        {
         MouseBlank=0L;
         if (WI_Config) set (CY_Mouse,MUIA_Cycle_Active,MouseBlank);
        }
      else BlankerWindow=NULL;

      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;
      BlankerClientMsg.bcm_Velocity=Velocity;
      BlankerClientMsg.bcm_FinalTimeOut=FinalTimeOut;
      /* try to start Client */
      if (BlankerClientPort=CreateBlankerClient(ASwarmClientProcess,
                                                &BlankerClientMsg))
       {
        TimeLeft=InitTimeLeft=10L*ClientTimeOut;
        AddTool (BlankerClientPort,DeleteBlankerClient,
                 (LONG)&BlankerClientMsg);
       }
     }
  }
}
