head	1.4;
access;
symbols;
locks
	mheinz:1.4; strict;
comment	@ * @;


1.4
date	94.07.12.16.57.08;	author mheinz;	state Exp;
branches;
next	1.3;

1.3
date	94.07.12.16.37.17;	author mheinz;	state Exp;
branches;
next	1.2;

1.2
date	94.02.07.17.44.57;	author mheinz;	state Exp;
branches;
next	1.1;

1.1
date	94.02.07.17.42.45;	author mheinz;	state Exp;
branches;
next	;


desc
@This is the main blanker program.
@


1.4
log
@Added copyright notice.
@
text
@/*
 * REyes 
 *
 * A screen blanker for the Retina graphics card.
 * By Michael Heinz, 8 January, 1994
 *
 * This software is FREEWARE.  It may be freely distributed for non-commercial use.
 * All rights are reserved by the author.
 *
 * Main routines
 *
 * $Log: reyes.c $
 * Revision 1.3  1994/07/12  16:37:17  mheinz
 * Fixed glitch in filling out NewBroker.
 * Switched Version string to RCS version
 *
 * Revision 1.2  1994/02/07  17:44:57  mheinz
 * Added RCS stuff.
 *
 */

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

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

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

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

#ifdef SMALLEYES
#include "smalleyes.h"
#else
#include "retinaeyes.h"
#endif

UBYTE *VersionString = "$VER: $Id: reyes.c 1.3 1994/07/12 16:37:17 mheinz Exp mheinz $";

#define NORMAL_WIDE 640
#define NORMAL_HIGH 480
#define SMALL_WIDE 320
#define SMALL_HIGH 240

void
chkabort(void)
{
}

/*
 * Common Definitions
 */

extern struct ExecBase *SysBase;
extern struct DosLibrary *DOSBase;
struct IntuitionBase *IntuitionBase;
struct RetinaBase *RetinaBase;
struct Library *CxBase, *GadToolsBase, *IconBase;

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


/*
 * A handy request structure for reporting that we've up and died.
 */
struct EasyStruct quitreq =
{
    sizeof(struct EasyStruct),
    0,
    "REyes",
    "REyes has suffered an untimely demise\ndue to: %s",
    "OK|OK"
};

void 
Error(char *s)
{
    if (s) EasyRequest(NULL,&quitreq,NULL,s);
}

/*
 * Definitions for our Commodity
 */
struct NewBroker NewBroker =
{NB_VERSION, "REyes ", NULL,
 "Somebody's watching you...", NBU_NOTIFY | NBU_UNIQUE, COF_SHOW_HIDE,
 0, NULL, 0};

struct MsgPort *CxPort;

UBYTE *PopKey;
UBYTE *BlankKey;

#define HOTKEY_OPEN_WINDOW 1L
#define HOTKEY_BLANK_SCREEN 2L

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

LONG TimeOut, ClientTimeOut;

#define MAX_TIMEOUT        3600L
#define MAX_CLIENT_TIMEOUT 60L

#define DEF_TIMEOUT        60L
#define DEF_CLIENT_TIMEOUT 5L

#define SERVER_PRI 3L
#define CLIENT_PRI -5L

/*
 * Definitions for Eye Movement
 */
#define RAND(m) (Random(m)-(m)/2)

#define MAX_SPEED     10L
#define MAX_EYES      10L
#define MAX_CHANCE	  100L

#define DEF_EYES      5L
#define DEF_SPEED     4L
#define DEF_CHANCE	  10L

#define DEF_MODE MID_DEFAULT_08

LONG NumEyes, Speed, Chance;

typedef struct {
    int NumEyes;
    Eyes *Eye[MAX_EYES];
} EyeStruct;


EyeStruct EyeList;

/*
 * Definitions for our configuration window
 */

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

struct Window *BlankerWindow;

#define GID_HIDE      	1
#define GID_BLANK     	2
#define GID_QUIT      	3
#define GID_TIMEOUT   	4
#define GID_CLIENT    	5
#define GID_EYES      	6
#define GID_SPEED     	7
#define GID_CHANCE	  	8

#ifdef SMALLEYES
#define NUM_GADS 		7
#else
#define NUM_GADS 		8
#endif

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

struct NewGadget NewBlankerGadgets[NUM_GADS] =
{16, 101, 48, 12, "_Hide", &BlankerAttr, GID_HIDE, PLACETEXT_IN, NULL, NULL,
 80, 101, 48, 12, "_Blank", &BlankerAttr, GID_BLANK, PLACETEXT_IN, NULL, NULL,
 144, 101, 48, 12, "_Quit", &BlankerAttr, GID_QUIT, PLACETEXT_IN, NULL, NULL,
 136, 5, 66, 12, "Timeout", &BlankerAttr, GID_TIMEOUT, PLACETEXT_LEFT, NULL, NULL,
 136, 21, 66, 12, "Client Timeout", &BlankerAttr, GID_CLIENT, PLACETEXT_LEFT, NULL, NULL,
 136, 53, 66, 12, "Speed    ", &BlankerAttr, GID_SPEED, PLACETEXT_LEFT, NULL, NULL,
 136, 69, 66, 12, "Eyes     ", &BlankerAttr, GID_EYES, PLACETEXT_LEFT, NULL, NULL,
#ifndef SMALLEYES
 136, 37, 66, 12, "%Anim    ", &BlankerAttr, GID_CHANCE, PLACETEXT_LEFT, NULL, NULL,
#endif
};

UBYTE BlankerGadgetKinds[NUM_GADS] =
{
	BUTTON_KIND, BUTTON_KIND, BUTTON_KIND, INTEGER_KIND, INTEGER_KIND, SLIDER_KIND,
	SLIDER_KIND, 
#ifndef SMALLEYES
	SLIDER_KIND
#endif
};

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

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

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

struct TagItem ChanceGadgetTags[] =
{GTSL_Level, 0L, GTSL_Min, 1L, GTSL_Max, MAX_CHANCE, GTSL_LevelFormat, 0L,
 GTSL_LevelPlace, PLACETEXT_LEFT, GTSL_MaxLevelLen, 3L, GA_RELVERIFY, TRUE,
 TAG_DONE, 0L};

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

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

struct TagItem *BlankerGadgetTagLists[NUM_GADS] =
{
	&ButtonGadgetTags[0],
 	&ButtonGadgetTags[0],
 	&ButtonGadgetTags[0],
 	&TimeGadgetTags[0],
 	&ClientGadgetTags[0],
 	&SpeedGadgetTags[0],
 	&EyesGadgetTags[0],
#ifndef SMALLEYES
 	&ChanceGadgetTags[0],
#endif
};

/*
 * Definitions for Server/Client Communication
 */

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

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

struct ClientMessage {
	struct Message bcm_Message;
	struct RetinaScreen *bcm_Screen;
	LONG bcm_Status;
	ULONG bcm_SigMask;
	LONG bcm_Eyes, bcm_Speed, bcm_Chance;
};

/*
 * The following functions are used to track resources.
 */
struct ToolNode {
	struct ToolNode *Next;
	void *Tool;
	void (*RemProc) (void *, LONG);
	LONG Size;
}

*ToolList;

void __regargs
RemTool(void *Tool)
{
	struct ToolNode *Ptr, *OPtr;

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

void __regargs
Quit(int ReturnCode, char *s)
{
	while (ToolList)
		RemTool(ToolList->Tool);
    if (ReturnCode) Error(s);
	exit(ReturnCode);
}

void __regargs
AddTool(void *NewTool, void *ProcPtr, LONG NewSize, char *errmsg)
{
	struct ToolNode *Ptr;
	void (*NewRemProc) (void *, LONG);

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

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

/*
 * Some utility functions
 */

void __stdargs
DeleteMsgPortSafely(struct MsgPort *AnyPort)
{
	struct Message *AnyMsg;

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

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

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

/*
 * The "InputHandler".  It signals the server process
 * when an input or timer event is received.
 */

void __interrupt __saveds
BlankerAction(CxMsg * CxMsg, CxObj * CO)
{
	struct InputEvent *IE;

	IE = (struct InputEvent *)CxMsgData(CxMsg);
	if (IE->ie_Class == IECLASS_TIMER)
		Signal(ServerProcess, 1L << bsp_TimerSig);
	else
		Signal(ServerProcess, 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 (BlankerWindow) {
		/* 
		 * We save the current position of the window
		 * so it will re-open in the same place, later.
		 */
		NewBlankerWindow.LeftEdge = BlankerWindow->LeftEdge;
		NewBlankerWindow.TopEdge = BlankerWindow->TopEdge;

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

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

	if (BlankerWindow == NULL) {
		strcpy(Title, "REyes = <");
		strcat(Title, PopKey);
		strcat(Title, ">");

		if (BlankerWindow = OpenWindowTags(&NewBlankerWindow, WA_Title, Title,
						   					WA_AutoAdjust, TRUE, WA_InnerWidth, 
											212, WA_InnerHeight, 118, TAG_DONE)) {
						   	
			AddTool(BlankerWindow, CloseWindow, NULL,"Could not open the window.");

			if ((BlankerVisualInfo = GetVisualInfo(BlankerWindow->WScreen, TAG_DONE)) 
				== NULL) {
				RemTool(BlankerWindow);
				return;
			}
			AddTool(BlankerVisualInfo, FreeVisualInfo, 0L, 
					"Couldn't get visual data.");

			BlankerGadgets = NULL;
			if ((Ptr = CreateContext(&BlankerGadgets)) == NULL) {
				RemTool(BlankerVisualInfo);
				RemTool(BlankerWindow);
				return;
			}
			AddTool(Ptr, FreeGadgets, 0L,"Couldn't allocate the gadgets.");

			/* Here we re-load the gadgets with the current settings. */
			TimeGadgetTags[0].ti_Data = (ULONG) TimeOut;
			ClientGadgetTags[0].ti_Data = (ULONG) ClientTimeOut;
			SpeedGadgetTags[0].ti_Data = (ULONG) Speed;
			SpeedGadgetTags[3].ti_Data = (ULONG) "%2ld";
#ifndef SMALLEYES
			ChanceGadgetTags[0].ti_Data = (ULONG) Chance;
			ChanceGadgetTags[3].ti_Data = (ULONG) "%3ld";
#endif
			EyesGadgetTags[0].ti_Data = (ULONG) NumEyes;
			EyesGadgetTags[3].ti_Data = (ULONG) "%2ld";

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

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

			AddGList(BlankerWindow, BlankerGadgets, 0L, -1L, NULL);
			RefreshGadgets(BlankerGadgets, BlankerWindow, NULL);
			GT_RefreshWindow(BlankerWindow, NULL);
		}
	}
	ScreenToFront(BlankerWindow->WScreen);
	WindowToFront(BlankerWindow);
	ActivateWindow(BlankerWindow);
}

/*
 * Function to handle the Commodity Stuff
 */

void __regargs
HandleCxMsg(CxObj * Broker, CxMsg * CxMsg, LONG * TimeUntilBlank,
			LONG * ThisTimeOut)
{
	ULONG MsgType, MsgID;

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

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

#ifdef OBSOLETE
/*
 * These functions have been replaced by defines.
 * They're provided here for hysterical raisins.
 */
void
SpritesOff(struct RetinaScreen *rs)
{
	struct TagItem tags[] =
	{RSP_On, 0L, TAG_DONE, 0L};

	Retina_SpriteFunction(rs, tags);
}

void
SpritesOn(struct RetinaScreen *rs)
{
	struct TagItem tags[] =
	{RSP_On, 1L, TAG_DONE, 0L};

	Retina_SpriteFunction(rs, tags);
}
#else
/*
 * We use defines for these functions just to pick up a little speed.
 */

#define SpritesOff(rs) 				\
{									\
	struct TagItem tags[] =			\
	{RSP_On, 0L, TAG_DONE, 0L};		\
									\
	Retina_SpriteFunction(rs, tags);\
}
	
#define SpritesOn(rs)				\
{									\
	struct TagItem tags[] =			\
	{RSP_On, 1L, TAG_DONE, 0L};		\
									\
	Retina_SpriteFunction(rs, tags);\
}
#endif

/*
 * This, annoyingly enough, is neccessary because Retina_CloseScreen is
 * actually a #pragma and not a real function call.  This means you
 * can't pass a pointer to Retina_CloseScreen as a function argument
 * to AddTool.  By adding this wrapper function, we make AddTool work.
 */
void
DestroyScreen(struct RetinaScreen *rs)
{
	Retina_CloseScreen(rs);
}

/*
 * Create Screen will first attempt to open a "normal" resolution screen.
 * If that fails, it will try opening a lower resolution screen.
 * If that fails it will return NULL.
 */
struct RetinaScreen *
CreateScreen(void)
{
	struct RetinaScreen *Screen;

	if (!(Screen = Retina_OpenScreen(NORMAL_WIDE, NORMAL_HIGH, MID_DEFAULT_08, 
									 RSFF_DOUBLEBUFFER, NULL))) 
		if (!(Screen = Retina_OpenScreen(SMALL_WIDE, SMALL_HIGH, MID_DEFAULT_08, 
										 RSFF_DOUBLEBUFFER, NULL))) 
			return NULL;
	
	Retina_LoadPalette(Screen, 0, EYES_COLS, color_table);
	SpritesOff(Screen);
	return Screen;
}

/*
 * Functions for Creating/Deleting the Client Process
 */

void __stdargs
DeleteBlankerClient(struct MsgPort * ClientPort)
{
	struct ClientMessage ClientMessage;

    /* Send the terminating message to the client. 
     * We kick up the client's priority to ensure a 
     * prompt response.
     */
    Forbid();
	ClientMessage.bcm_Message.mn_ReplyPort = FINDPROCPORT;
	PutMsg(ClientPort, (struct Message *)&ClientMessage);
    
	(void)SetTaskPri(ClientPort->mp_SigTask, SERVER_PRI);
    Permit();
    
    /* Get the response. */
	(void)WaitPort(ClientMessage.bcm_Message.mn_ReplyPort);
	(void)GetMsg(ClientMessage.bcm_Message.mn_ReplyPort);
}

struct MsgPort *__regargs
CreateBlankerClient(void *ClientRoutine, struct ClientMessage *ClientMessage)
{
	struct Process *ClientProcess;
	struct TagItem ProcTags[3];

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

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

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

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

		if (ClientMessage->bcm_Status)
			return &ClientProcess->pr_MsgPort;
	}
	return NULL;
}

/*
 * Functions for Creating/Drawing/Removing the Eyes
 */

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

/* 
 * This is a fairly high-speed psuedo random number generator,
 * but it's not to random, though.  Occasionally, it will
 * be non-random enough for a while to cause all the Eyes to
 * move in the same direction (or some such.)  I use it
 * because it's fast and because, with all the collision 
 * detection done now, it's less likely the un-randomness
 * will be noticed.
 */
WORD __regargs
Random(WORD Max)
{
	static ULONG Num = 0L;
	ULONG Sec, Mic;

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

	Num *= Sec;
	Num += Mic;

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

	return (WORD)(Num % Max);
}

/*
 * Free up the list of eyes.
 */
void __regargs
DeleteEyes(EyeStruct *EyeList)
{
	LONG i;

    for (i=0;i<EyeList->NumEyes;i++) 
		if (EyeList->Eye[i]!=NULL) FreeMem(EyeList->Eye[i],sizeof(Eyes));
}

/*
 * Create the list of eyes.
 */
EyeStruct *__regargs
CreateEyes(struct RetinaScreen *Screen,
			LONG NumEyes, LONG Speed)
{
	int Index, i, flag, count;
    Eyes *randomeyes[4];
    
    if (!Screen) return NULL;
    
    randomeyes[0]=&BlueEyes;
    randomeyes[1]=&GreenEyes;
    randomeyes[2]=&VioletEyes;
    randomeyes[3]=&BrownEyes;

	/*
	 * Create the Eyes
	 */
	for (Index = 0; Index < NumEyes; Index++) {
        if (!(EyeList.Eye[Index]=AllocMem(sizeof(Eyes),MEMF_PUBLIC|MEMF_CLEAR))) 
			Quit(20,"Out of Memory!!!");
        CopyMem(randomeyes[Random(4)],EyeList.Eye[Index],sizeof(Eyes));
#ifndef SMALLEYES
        EyeList.Eye[Index]->currentframe=Random(EyeList.Eye[Index]->maxframes);
#else
        EyeList.Eye[Index]->currentframe=0;
#endif
       	EyeList.Eye[Index]->xspeed=Insure1(RAND(Speed));
       	EyeList.Eye[Index]->yspeed=Insure1(RAND(Speed));
		count=0; flag=1;

		/* 
		 * We try 4 times to fit an eye pair on the screen.  If we can't, then
		 * the screen must be too small for the # of eyes and we truncate the list.
		 */
		while (flag & count<4) {
        	EyeList.Eye[Index]->xpos=Random(Screen->rs_Width-
											EyeList.Eye[Index]->width);
        	EyeList.Eye[Index]->ypos=Random(Screen->rs_Height-
											EyeList.Eye[Index]->height);
			flag=0;
			for (i=0;i<Index; i++) {
				/* Looking for overlapping rectangles... */
				if ((EyeList.Eye[i]->xpos>
					 EyeList.Eye[Index]->xpos-EyeList.Eye[i]->width)&&
					(EyeList.Eye[i]->xpos<
					 EyeList.Eye[Index]->xpos+EyeList.Eye[Index]->width)&&
					(EyeList.Eye[i]->ypos>
					 EyeList.Eye[Index]->ypos-EyeList.Eye[i]->height)&&
					(EyeList.Eye[i]->ypos<
					 EyeList.Eye[Index]->ypos+EyeList.Eye[Index]->height)) {
					flag=1;
					break;
				}
			}
		}
		if (flag) {	/* If we failed to add an Eye pair, truncate the list. */
			NumEyes=Index+1;
			break;
		}
	}

    EyeList.NumEyes=NumEyes;
    
	return &EyeList;
}

/* move the Eyes and redraw them */

void __regargs
DrawEyes(EyeStruct *EyeList, struct RetinaScreen *rs, LONG Chance)
{
	int Index,i;
	Eyes **EL;

	/* 
	 * This assignment provides a (very mild) speed up buy removing
	 * one level of indirection from all the work that's going on below.
	 */
	EL=EyeList->Eye;

	Chance=Insure1(100-Chance);

	/* If we don't have a screen, don't draw on it! (duh.) */
	if (!rs) return; 

    for (Index=0; Index<EyeList->NumEyes; Index++) {
        /* Move the Eye. */
        EL[Index]->old_xpos=EL[Index]->xpos;
        EL[Index]->old_ypos=EL[Index]->ypos;
        EL[Index]->xpos+=EL[Index]->xspeed;
        EL[Index]->ypos+=EL[Index]->yspeed;
        
	    /* Bounce check */
        if (EL[Index]->xpos<0) { 
            EL[Index]->xpos=0; 
	        EL[Index]->xspeed=Insure1(RAND(Speed));
        } else if (EL[Index]->xpos>rs->rs_Width-EL[Index]->width-1) {
            EL[Index]->xpos=rs->rs_Width-EL[Index]->width-1;
	        EL[Index]->xspeed=Insure1(RAND(Speed));
		}
        if (EL[Index]->ypos<0) { 
            EL[Index]->ypos=0; 
	        EL[Index]->yspeed=Insure1(RAND(Speed));
        } else if (EL[Index]->ypos>rs->rs_Height-EL[Index]->height-1) {
            EL[Index]->ypos=rs->rs_Height-EL[Index]->height-1;
	        EL[Index]->yspeed=Insure1(RAND(Speed));
        }

		/* 
		 * Collision check... 
		 * If we detect that we hit one other eye pair, we reverse direction.
		 * Note - checking stops after the first collision, otherwise
		 * multiple-collisions might cause the eye pair to continue in
		 * the same direction (i.e., its direction got negated twice)
		 */
		for (i=0;i<EyeList->NumEyes; i++) {
			if (i!=Index) {
				if ((EL[i]->xpos>EL[Index]->xpos-EL[i]->width)&&
					(EL[i]->xpos<EL[Index]->xpos+EL[Index]->width)&&
					(EL[i]->ypos>EL[Index]->ypos-EL[i]->height)&&
					(EL[i]->ypos<EL[Index]->ypos+EL[Index]->height)) {

	        		EL[Index]->yspeed= (EL[Index]->yspeed>0)?-Insure1(Random(Speed/2)):Insure1(Random(Speed/2));
	        		EL[Index]->xspeed= (EL[Index]->xspeed>0)?-Insure1(Random(Speed/2)):Insure1(Random(Speed/2));
        			EL[Index]->xpos=EL[Index]->old_xpos;
        			EL[Index]->ypos=EL[Index]->old_ypos;
					break;
				}
			}
		}
#ifndef SMALLEYES
		/* Are we animating this Eye pair? */
	    if (EL[Index]->currentframe) {
			if (!EL[Index]->tic) {
				EL[Index]->tic++;
			} else {
				EL[Index]->tic=0;
				EL[Index]->currentframe=(EL[Index]->currentframe+1)%
									EL[Index]->maxframes;
			}
		/* No?  Then roll the dice and see if we should start... */
		} else if (!Random(Chance)) { 
			EL[Index]->currentframe=1;
		}
#endif
			
	}

    /* Redraw on the background bitmap... */
    for (Index=0; Index<EyeList->NumEyes; Index++) {
        Retina_WriteRect(EL[Index]->animtable[EL[Index]->currentframe],
						 0,0,
                         EL[Index]->width, RECTMODE_256,
                         rs,EL[Index]->xpos, EL[Index]->ypos,
                         EL[Index]->width, EL[Index]->height, NULL);   
        
	}
        
	/* Swap the bitmaps */
	Retina_SwapBitMap(rs);

	/* Erase the old images on the new background bitmap. */
    for (Index=0; Index<EyeList->NumEyes; Index++) {
        Retina_RectFill(rs,EL[Index]->old_xpos,
						EL[Index]->old_ypos,
                        EL[Index]->old_xpos+EL[Index]->width,
                        EL[Index]->old_ypos+EL[Index]->height);
	}
}

/*
 * This is the Client Process's Main Loop
 */

void __interrupt __saveds
REyesClientProcess(void)
{
	struct ClientMessage *ClientMessage;
	struct MsgPort *ClientPort;
	struct Task *ServerTask;
	ULONG ServerSigMask;
	struct RetinaScreen *EyesScreen;
	LONG NumEyes, Speed, Chance;
	EyeStruct *EyeList;

	/* wait for Server's initial Message */

	ClientPort = FINDPROCPORT;
	(void)WaitPort(ClientPort);
	ClientMessage = (struct ClientMessage *)GetMsg(ClientPort);

	ServerTask = ClientMessage->bcm_Message.mn_ReplyPort->mp_SigTask;
	ServerSigMask = ClientMessage->bcm_SigMask;

	NumEyes = ClientMessage->bcm_Eyes;
	Speed = ClientMessage->bcm_Speed;
	Chance = ClientMessage->bcm_Chance;
	EyesScreen = ClientMessage->bcm_Screen;

	if (EyesScreen) {
		if ((EyeList = CreateEyes(EyesScreen, NumEyes, Speed))== NULL) {
			ClientMessage->bcm_Status = FALSE;
			Forbid();
			ReplyMsg((struct Message *)ClientMessage);
			return;
		}
	}
            
	ClientMessage->bcm_Status = TRUE;
	ReplyMsg((struct Message *)ClientMessage);

	/* Animate the blanker screen until a termination message is received. */
	while ((ClientMessage = (struct ClientMessage *)GetMsg(ClientPort)) == NULL) {
		if (EyesScreen) DrawEyes(EyeList, EyesScreen, Chance);
	
		/* 
		 * We signal the server after each loop so it will know that we're alive.
		 */
		Signal(ServerTask, ServerSigMask);
	}

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

	if (EyeList) DeleteEyes(EyeList);
	Forbid();
	ReplyMsg((struct Message *)ClientMessage);
}

/*
 * The Main Loop
 */

void
main(LONG argc, UBYTE * argv[])
{
	char **ToolTypes;
	CxObj *Broker, *ObjectList, *Filter;
	struct IntuiMessage *IntMsg;
	CxMsg *BlankerCxMsg;
	LONG ThisTimeOut, TimeUntilBlank, TimeUntilBlack;
	struct RetinaScreen *BlankerScreen = NULL;
	struct MsgPort *ClientPort = NULL;
    int BlankFlag=0;

	/* open our Libraries */
	AddTool(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",
											  37L), CloseLibrary, 0L,
            "Couldn't open the intuition.library.");
	AddTool(IconBase = OpenLibrary("icon.library", 37L), CloseLibrary, 0L,
            "Couldn't open the icon library.");
	AddTool(CxBase = OpenLibrary("commodities.library", 37L), CloseLibrary, 0L,
            "Couldn't open the commoidities library.");
	AddTool(GadToolsBase = OpenLibrary("gadtools.library", 37L), CloseLibrary, 0L,
            "Couldn't open the gadtools library.");
	AddTool(RetinaBase = (struct _xy_RetinaBase *)OpenLibrary("retina.library", 7L), 
            CloseLibrary, 0L,"Couldn't open the Retina library.");

	/* get our Arguments */

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

	/* get some Signals */

	ServerProcess = SysBase->ThisTask;
	if ((bsp_TimerSig = AllocSignal(-1L)) == -1)
		Quit(10,"Could not allocate a signal.");
	AddTool((void *)bsp_TimerSig, FreeSignal, 0L,NULL);
	if ((bsp_InputSig = AllocSignal(-1L)) == -1)
		Quit(10,"Could not allocate a signal.");
	AddTool((void *)bsp_InputSig, FreeSignal, 0L,NULL);
	if ((bsp_ClientSig = AllocSignal(-1L)) == -1)
		Quit(10,"Could not allocate a signal.");
	AddTool((void *)bsp_ClientSig, FreeSignal, 0L,NULL);

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

	AddTool(CxPort = CreateMsgPort(), DeleteMsgPortSafely, 0L, 
			"Failed to create a port.");

    VersionString[36]='\0'; /* This string is filled out by RCS.  We need to shorten it. */
    NewBroker.nb_Title=&VersionString[11];
	NewBroker.nb_Pri = ArgInt(ToolTypes, "CX_PRIORITY", DEF_CX_PRI);
	NewBroker.nb_Port = CxPort;
	AddTool(Broker = CxBroker(&NewBroker, NULL), DeleteCxObjAll, 0L, 
            NULL);

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

	TimeOut = ArgIntMax(ToolTypes, "TIMEOUT", DEF_TIMEOUT, MAX_TIMEOUT);
	ClientTimeOut = ArgIntMax(ToolTypes, "CLIENTTIMEOUT", DEF_CLIENT_TIMEOUT, 
								MAX_CLIENT_TIMEOUT);

	/* get Parameters for Eye Movement */

	NumEyes = ArgIntMax(ToolTypes, "EYES", DEF_EYES, MAX_EYES);
	Speed = ArgIntMax(ToolTypes, "SPEED", DEF_SPEED, MAX_SPEED);
	Chance = ArgIntMax(ToolTypes, "%ANIM", DEF_CHANCE, MAX_CHANCE);

	/* install our hot keys */

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

	if ((Filter = HotKey(PopKey, CxPort, HOTKEY_OPEN_WINDOW)) == NULL)
		Quit(10,"The CX_POPKEY tool type is invalid.");
	else
		AttachCxObj(Broker, Filter);
	if (CxObjError(Filter))
		Quit(10,"Could not link to the commodity list.");

	if ((Filter = HotKey(BlankKey, CxPort, HOTKEY_BLANK_SCREEN)) == NULL)
		Quit(10,"The BLANKKEY tool type is invalid.");
	else
		AttachCxObj(Broker, Filter);
	if (CxObjError(Filter))
		Quit(10,"Could not link to the commodity list.");

	/* install our "InputHandler" */

	ObjectList = CxCustom(BlankerAction, 0L);
	AttachCxObj(Broker, ObjectList);
	if (CxObjError(ObjectList))
		Quit(10,"Could not link to the commodity list.");

	(void)ActivateCxObj(Broker, TRUE);
	AddTool(Broker, ActivateCxObj, 0L,"The broker broke.");

	/* open Window on startup if not forbidden */

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

	/* 
     * increase our Priority 
     * Note that when we quit, our priority will be reset to what it
     * was before.
     */

	AddTool(FindTask(NULL), SetTaskPri, (LONG) SetTaskPri(FindTask(NULL), SERVER_PRI),
            "Findtask failed!");

	/* 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,"Normal Termination.");
						case GID_BLANK:
							TimeUntilBlank = ThisTimeOut = 2L;
							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_EYES:
							NumEyes = Code;
							break;
						case GID_SPEED:
							Speed = Code;
							break;
						case GID_CHANCE:
							Chance = Code;
							break;
					}
					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,"Normal Termination.");
						case 'B':
						case 'b':
							TimeUntilBlank = ThisTimeOut = 2L;
					}
					break;
				default:
					GT_ReplyIMsg(IntMsg);
			}

		/* process Commodity Messages */

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

		/* check for <CTRL>-C */

		if (Mask & SIGBREAKF_CTRL_C)
			Quit(0,"Normal Termination.");

		/* Input detected, unblank if necessary */

		if (Mask & MASK(bsp_InputSig)) {
			if (BlankFlag) {
				if (ClientPort)
					RemTool(ClientPort);
				
                if (BlankerScreen) {
					SpritesOn(BlankerScreen);
                    RemTool(BlankerScreen);
                } else
                    Retina_DisplayOn();
                BlankFlag=0;
				ThisTimeOut = 10L * TimeOut;
			}
			TimeUntilBlank = ThisTimeOut;
		}

		/* client has confirmed that it is still alive */
		if (Mask & MASK(bsp_ClientSig)) {
			if (BlankerScreen) Retina_DisplayOn();
			TimeUntilBlack = 10L * ClientTimeOut;
            BlankFlag=1;
		}
		/* 1/10 sec is over */

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

                    BlankFlag=1;
                    BlankerScreen=CreateScreen();
					if (BlankerScreen) 
                        AddTool(BlankerScreen, DestroyScreen, 0L,NULL);
                    else
						/* do this if we're completely out of video memory. */
                        Retina_DisplayOff();  

					ClientMessage.bcm_Screen = BlankerScreen;
					ClientMessage.bcm_SigMask = 1L << bsp_ClientSig;
					ClientMessage.bcm_Eyes = NumEyes;
					ClientMessage.bcm_Speed = Speed;
					ClientMessage.bcm_Chance = Chance;

					if (ClientPort = CreateBlankerClient(REyesClientProcess, 
						&ClientMessage)) {
						TimeUntilBlack = 10L * ClientTimeOut;	
						/* try to start Client */
						AddTool(ClientPort, DeleteBlankerClient, 0L,NULL);
					}
				}
			} else {
				if ((BlankerScreen)&&(RetinaBase->rb_FirstScreen != BlankerScreen)) {
					Retina_ScreenToFront(BlankerScreen);
					SpritesOff(BlankerScreen);
				}
				if (TimeUntilBlack) {
					TimeUntilBlack--;
					if (TimeUntilBlack == 0L)
						Retina_DisplayOff();	/* Client Time Out
												 * reached, turn entire
												 * screen black */
				}
			}
	}
}
@


1.3
log
@Fixed glitch in filling out NewBroker.
Switched Version string to RCS version
@
text
@d7 3
d12 5
a16 1
 * $Log: REyes.c $
d43 1
a43 1
#include <retina/retina.h>
d62 1
a62 1
UBYTE *VersionString = "$VER: $Id$";
@


1.2
log
@Added RCS stuff.
@
text
@d9 4
a12 1
 * $Log$
d55 1
a55 1
UBYTE *VersionString = "$VER: REyes 1.3 (By Michael Heinz)";
d186 1
a186 1
struct Gadgets *BlankerGadgets;
d282 1
a282 1
	struct ToolNode **Ptr, *ToolNode;
d284 16
a299 9
	Ptr = &ToolList;
	while (*Ptr)
		if ((*Ptr)->Tool == Tool) {
			ToolNode = *Ptr;
			*Ptr = (*Ptr)->Next;
			ToolNode->RemProc(ToolNode->Tool, ToolNode->Size);
			FreeMem(ToolNode, sizeof(struct ToolNode));
		} else
			Ptr = &(*Ptr)->Next;
d683 1
a683 1
	CurrentTime((LONG *) & Sec, (LONG *) & Mic);
d691 1
a691 1
	return (WORD) Num % Max;
d989 2
a990 1
    NewBroker.nb_Descr=&VersionString[6];
d1042 5
a1046 1
	/* increase our Priority */
@


1.1
log
@Initial revision
@
text
@d9 1
@
