DOS 2.0 Utilities Disk 5 (Feb 1992) : FracBlank / FracBlank.c

/* $Revision Header * Header built automatically - do not edit! *************
 *
 *	(C) Copyright 1991 by Olaf Barthel & MXM
 *
 *	Name .....: FracBlank.c
 *	Created ..: Friday 18-Jun-91 15:28
 *	Revision .: 8
 *
 *	Date            Author          Comment
 *	=========       ========        ====================
 *	09-Sep-91	Olsen		Numerous bug fixes.
 *	17-Aug-91	Olsen		Rewrote sprite blanker code.
 *	17-Aug-91	Olsen		Added cycle gadget.
 *	12-Aug-91	Olsen		Added multicolour/monochrome mode.
 *	30-Jun-91	Olsen		VBlank code revised.
 *	29-Jun-91	Olsen		Added VBlank interrupt server.
 *	21-Jun-91	Olsen		Optimizations, cleanups.
 *	18-Jun-91	Olsen		Created this file!
 *
 * $Revision Header ********************************************************/

	/* Include the specific math pragmas/header files here (is there
	 * any way to figure this out by taking a look at predefined
	 * compiler symbols?).
	 */

#ifdef MATH_FFP
#include <clib/mathtrans_protos.h>
#include <proto/mathtrans.h>
#include <mffp.h>
#else
#include <m68881.h>
#endif	/* MATH_FFP */

#include <math.h>

	/* sin -45° = cos -45° (saves precious calculation time). */

#define deg45	(-0.707106781)

	/* Hotkey IDs. */

enum	{	POP_WINDOW,BLANK_SCREEN };

	/* Gadget IDs. */

enum	{	GAD_SCREENTIMEOUT,GAD_PATTERNCHANGE,GAD_HOTKEY,
		GAD_BLANKSCREEN,GAD_MONO,GAD_CYCLE,GAD_HIDE,GAD_QUIT };

	/* Dimensions of the control panel. */

#define WIDTH	284
#define HEIGHT	113

	/* Program revision tag. */

STATIC const UBYTE * const VersTag = "\0$VER: FracBlank 1.8 (9.9.91)";

	/* Shared library identifiers. */

extern struct ExecBase	*SysBase;
struct IntuitionBase	*IntuitionBase;
struct GfxBase		*GfxBase;
struct Library		*CxBase,
			*IconBase,
			*GadToolsBase;

	/* Blanker data. */

struct Task		*BlankTask;
struct Screen		*BlankScreen;

	/* Commodities interface data. */

struct MsgPort		*CxPort;
CxObj			*Broker;

	/* Gfx and gadtools data. */

struct Screen		*DefaultScreen;
APTR			 VisualInfo;
struct TextFont		*Topaz;
struct Gadget		*GadgetList;
struct Gadget		*GadgetArray[8];
struct Window		*Window;

	/* Window zoom data. */

WORD			 ZoomData[4] = { -1,-1,-1,-1 };

	/* Rainbow colour table. */

UWORD			 Table[75];

	/* Key sequence buffers. */

UBYTE			 HotkeyBuffer[256],BlankScreenBuffer[256];

	/* Screen and pattern change timeout. */

ULONG			 ScreenCount = 0,PatternCount = 0,ScreenTimeout = 60,PatternTimeout = 60;

	/* Display mode: monochrome / multicolor. */

BYTE			 Mono = FALSE,Cycle = TRUE;

	/* The default font to be used by the control panel. */

struct TextAttr DefaultFont =
{
	(UBYTE *)"topaz.font",
	8,
	FS_NORMAL,
	FPF_ROMFONT
};

	/* A new broker definition, Commodities needs this. */

struct NewBroker NewBroker =
{
	NB_VERSION,
	"FracBlanker",
	"Fractal Blanker v1.8",
	"Screen Blanker",
	NBU_NOTIFY | NBU_UNIQUE,
	COF_SHOW_HIDE,
	0,NULL,0
};

	/* Function prototypes. */

extern VOID __asm	MonoPlot(register __a0 PLANEPTR Plane,register __d0 WORD x,register __d1 WORD y,register __d2 UWORD Modulo,register __d3 WORD MaxX,register __d4 WORD MaxY);
extern VOID __asm	MultiPlot(register __a0 struct Screen *,register __d0 WORD X,register __d1 WORD Y,register __d2 WORD Colour);

VOID			StartBlanker(VOID);
ULONG __regargs		Random(ULONG MaxValue);
VOID __saveds		Blanker(VOID);
VOID __saveds __stdargs	BlankerAction(CxMsg *CxMessage,CxObj *CxObject);
VOID			ShutdownCx(VOID);
BYTE __regargs		SetupCx(UBYTE **ToolTypes);
VOID __regargs		HandleCxMsg(CxMsg *Message);
LONG __saveds __stdargs	ShowTime(struct Gadget *SomeGadget,WORD Level);
struct Gadget *		CreateAllGadgets(struct Gadget **GadgetArray,struct Gadget **GadgetList,APTR VisualInfo,UWORD TopEdge);
VOID			ShutdownWindow(VOID);
VOID __regargs		CentreWindow(struct Screen *Screen,WORD *LeftEdge,WORD *TopEdge);
BYTE			SetupWindow(VOID);
VOID __regargs		CloseAll(LONG ReturnCode);
VOID __regargs		OpenAll(int argc,char **argv);
VOID __stdargs		main(int argc,char **argv);

	/* StartBlanker():
	 *
	 *	Open the display screen and create the blanker task.
	 */

VOID
StartBlanker()
{
	if(!BlankScreen)
	{
		struct Screen *Screen;

		if(Screen = OpenScreenTags(NULL,
			SA_Behind,	TRUE,
			SA_Quiet,	TRUE,
			SA_DisplayID,	Mono ? HIRESLACE_KEY : LORES_KEY,
			SA_Overscan,	OSCAN_MAX,
			SA_Depth,	Mono ? 1 : 5,
		TAG_DONE))
		{
			struct UCopList	*UserCopperList;

				/* I had a lot of problems trying to shut down
				 * the sprite display hardware completely.
				 * The mouse pointer would pop up again after
				 * a certain time interval, no matter what I
				 * did.
				 *
				 * Now, Amy, you had your chance, hadn't you?
				 * 
				 * This is the big one: a user copper list will
				 * shut down the sprite hardware. Full stop.
				 */

			if(UserCopperList = (struct UCopList *)AllocMem(sizeof(struct UCopList),MEMF_PUBLIC|MEMF_CLEAR))
			{
					/* Set up for 3 instruction. */

				CINIT(UserCopperList,3);

					/* Wait for first line. */

				CWAIT(UserCopperList,0,0);

					/* Disable sprite DMA. */

				CMOVE(UserCopperList,custom . dmacon,BITCLR|DMAF_SPRITE);

					/* Terminate copper instruction list. */

				CEND(UserCopperList);

					/* Install the copper list. */

				Screen -> ViewPort . UCopIns = UserCopperList;

					/* Link it into the screen copper list. */

				RethinkDisplay();

					/* Set the background colour to black. */

				SetRGB4(&Screen -> ViewPort,0,0x0,0x0,0x0);

					/* Display the screen. */

				ScreenToFront(Screen);

				BlankScreen = Screen;
			}
			else
				CloseScreen(Screen);
		}
	}

	if(BlankScreen)
	{
		PatternCount = 0;

		BlankTask = (struct Task *)CreateTask("FracBlank.task",-20,Blanker,4000);
	}
}

	/* Random(ULONG MaxValue):
	 *
	 *	Simple random number generation routine.
	 */

ULONG __regargs
Random(ULONG MaxValue)
{
	STATIC ULONG RandomSeed = 0xDEAD0123;

	RandomSeed = RandomSeed * custom . vhposr + 0xE153766F;

	return(RandomSeed % MaxValue);
}

	/* Blanker():
	 *
	 *	The screen blanker itself.
	 */

VOID __saveds
Blanker()
{
	STATIC BYTE Wheel = 0;

	UWORD	Colours[32],OffsetX = BlankScreen -> Width >> 1,OffsetY = BlankScreen -> Height >> 1;
	float	x = 0,y = 0,yy,a,b,c,sx,sy,mag,save;

	SetSignal(0,SIGBREAKF_CTRL_D|SIGBREAKF_CTRL_E);

		/* Are we running in monochrome mode? */

	if(Mono)
	{
		UWORD		Modulo = BlankScreen -> RastPort . BitMap -> BytesPerRow;
		PLANEPTR	Plane = BlankScreen -> RastPort . BitMap -> Planes[0];

			/* Provide starting numbers for the fractal
			 * parameters.
			 */

		a = (float)Random(300) / 100;
		b = (float)Random(100) / 100;
		c = (float)Random( 50) / 100;

		mag = (float)(1 << (Random(4) + 5)) * deg45;

			/* Set up the screen colour table. */

		Colours[0] = 0;
		Colours[1] = Table[Wheel];

		LoadRGB4(&BlankScreen -> ViewPort,Colours,2);

			/* Go into fractal generation loop. */

		FOREVER
		{
				/* The original formula looks like
				 * this:
				 *                                     ½
				 *    x <- y - SIGN(x) * ABS(b * x - c)
				 *    y <- a - x
				 *
				 * I have split the calculation into
				 * several steps to save time and
				 * variables.
				 */

			yy = a - x;

			if((save = b * x - c) < 0)
				save = -save;

			if(x < 0)
				x = y + sqrt(save);
			else
				x = y - sqrt(save);

			y = yy;

				/* The resulting image appears to have
				 * been rotated by 45°, so we'll
				 * rotate the pixel coordinates by -45°
				 *
				 *    x <-  x * cos alpha + y * sin alpha
				 *    y <- -x * sin alpha + y * cos alpha
				 *
				 * We also magnify the image (i.e. the
				 * distribution of pixels) in the following
				 * lines.
				 */

			sx = mag * ( x + y);
			sy = mag * (-x + y);

				/* If the pixel happens to reside within
				 * the boundaries of the screen, draw it.
				 */

			MonoPlot(Plane,(WORD)(sx) + OffsetX,(WORD)(sy) + OffsetY,Modulo,BlankScreen -> Width,BlankScreen -> Height);

				/* ^D tells the blanker to change the pattern. */

			if(SetSignal(0,0) & SIGBREAKF_CTRL_D)
			{
				SetSignal(0,SIGBREAKF_CTRL_D);

				SetRast(&BlankScreen -> RastPort,0);

				x = y = 0;

				a = (float)Random(300) / 100;
				b = (float)Random(100) / 100;
				c = (float)Random( 50) / 100;

				mag = (float)(1 << (Random(4) + 5)) * deg45;
			}

				/* ^E tells the blanker to rotate the
				 * colours.
				 */

			if(SetSignal(0,0) & SIGBREAKF_CTRL_E)
			{
				SetSignal(0,SIGBREAKF_CTRL_E);

				Colours[1] = Table[Wheel];

				LoadRGB4(&BlankScreen -> ViewPort,Colours,2);

				Wheel = (Wheel + 1) % 75;
			}
		}
	}
	else
	{
		STATIC UWORD Rainbow[32] =
		{
			0x0000,0x0F00,0x0F30,0x0F50,
			0x0F70,0x0F90,0x0FB0,0x0FD0,
			0x0FF0,0x0DF0,0x0BF0,0x09F0,
			0x07F0,0x05F0,0x03F0,0x00F0,
			0x00D1,0x00B3,0x0095,0x0077,
			0x0059,0x003B,0x001D,0x000F,
			0x010F,0x030F,0x050F,0x070F,
			0x090F,0x0B0F,0x0D0F,0x0F0F
		};

		UWORD		 Count = 0;
		WORD		 i;
		BYTE		 Colour = 1,Plus = 1;
		struct RastPort	*RPort;

		a = (float)Random(300) / 100;
		b = (float)Random(100) / 100;
		c = (float)Random( 50) / 100;

		mag = (float)(1 << (Random(4) + 2)) * deg45;

		Colours[0] = 0x000;

		for(i = 1 ; i < 32 ; i++)
			Colours[i] = Table[(Wheel + i) % 75];

		if(Cycle)
			LoadRGB4(&BlankScreen -> ViewPort,Colours,32);
		else
			LoadRGB4(&BlankScreen -> ViewPort,Rainbow,32);

		Wheel = (Wheel + 1) % 75;

		RPort = &BlankScreen -> RastPort;

		FOREVER
		{
			yy = a - x;

			if((save = b * x - c) < 0)
				save = -save;

			if(x < 0)
				x = y + sqrt(save);
			else
				x = y - sqrt(save);

			y = yy;

			sx = mag * ( x + y);
			sy = mag * (-x + y);

			MultiPlot(BlankScreen,(WORD)(sx) + OffsetX,(WORD)(sy) + OffsetY,Colour);

				/* Oh well, it's not that easy to
				 * produce decent colour values for
				 * the pixels to be rendered.
				 *
				 * The following statement will change
				 * the current drawing pen after exactly
				 * 900 pixels have been rendered and will
				 * pick a new colour between 1 and 31.
				 */

			if(Count++ >= 900)
			{
				Count = 0;

				Colour += Plus;

				if(!Colour)
				{
					Plus	= 1;
					Colour	= 2;
				}
				else
				{
					if(Colour == 32)
					{
						Plus	= -1;
						Colour	= 30;
					}
				}
			}

			if(SetSignal(0,0) & SIGBREAKF_CTRL_D)
			{
				SetSignal(0,SIGBREAKF_CTRL_D);

				SetRast(RPort,0);

				x = y = 0;

				a = (float)Random(300) / 100;
				b = (float)Random(100) / 100;
				c = (float)Random( 50) / 100;

				mag = (float)(1 << (Random(4) + 2)) * deg45;
			}

			if(SetSignal(0,0) & SIGBREAKF_CTRL_E)
			{
				SetSignal(0,SIGBREAKF_CTRL_E);

				for(i = 1 ; i < 32 ; i++)
					Colours[i] = Table[(Wheel + i) % 75];

				if(Cycle)
					LoadRGB4(&BlankScreen -> ViewPort,Colours,32);

				Wheel = (Wheel + 1) % 75;
			}
		}
	}

		/* Quietly remove ourselves. */

	Forbid();

	BlankTask = NULL;

	RemTask(SysBase -> ThisTask);
}

	/* BlankerAction(CxMsg *CxMessage,CxObj *CxObject):
	 *
	 *	Commodities support routine, handles the Commodities
	 *	custom actions (in this case: filter the InputEvents
	 *	coming in and enable/disable the screen blanker).
	 */

VOID __saveds __stdargs
BlankerAction(CxMsg *CxMessage,CxObj *CxObject)
{
	STATIC BYTE Count = 1;

	struct InputEvent *Event = (struct InputEvent *)CxMsgData(CxMessage);

		/* Push the blanker screen to the front if necessary. */

	if(BlankScreen)
	{
		if(BlankScreen -> TopEdge)
			MoveScreen(BlankScreen,0,-BlankScreen -> TopEdge);

		if(IntuitionBase -> FirstScreen != BlankScreen)
			ScreenToFront(BlankScreen);
	}

		/* This looks like a timer event. */

	if(Event -> ie_Class == IECLASS_TIMER && !Window)
	{
			/* Screen blanker still inactive? */

		if(!BlankTask)
		{
				/* Is there a timeout to take care of? */

			if(ScreenTimeout)
			{
					/* Are we ready to create the
					 * screenblanker?
					 */

				if(ScreenCount++ >= ScreenTimeout * 10)
				{
					if(!BlankTask)
						StartBlanker();
				}
			}
		}
		else
		{
			if(Count < 0)
				Count = 0;

				/* Every 5/60 second we signal the blanker
				 * task to rotate the palette.
				 */

			if(Count++ >= 2)
			{
				Signal(BlankTask,SIGBREAKF_CTRL_E);

				Count = 0;
			}

				/* Is it time to change the pattern? */

			if(PatternTimeout)
			{
				if(PatternCount++ >= PatternTimeout * 10)
				{
					Signal(BlankTask,SIGBREAKF_CTRL_D);

					PatternCount = 0;
				}
			}
		}
	}
	else
	{
			/* The following line determines whether
			 * the blanker is to be removed or to
			 * be left running.
			 */

		if((Event -> ie_Class == IECLASS_RAWKEY && !(Event -> ie_Code & IECODE_UP_PREFIX) && !(Event -> ie_Qualifier & IEQUALIFIER_REPEAT)) || Event -> ie_Class == IECLASS_RAWMOUSE)
		{
				/* Remove the blanker task. */

			if(BlankTask)
			{
				struct Task *Task = BlankTask;

				BlankTask = NULL;

				RemTask(Task);
			}

				/* Close the blanker screen. */

			if(BlankScreen)
			{
				struct Screen *Screen = BlankScreen;

				BlankScreen = NULL;

				ScreenToBack(Screen);

				FreeVPortCopLists(&Screen -> ViewPort);

				RemakeDisplay();

				CloseScreen(Screen);
			}
		}

		ScreenCount = 0;
	}
}

	/* ShutdownCx():
	 *
	 *	Close the Commodities interface.
	 */

VOID
ShutdownCx()
{
	if(CxPort)
	{
		struct Message *Message;

			/* Remove the broker. */

		if(Broker)
			DeleteCxObjAll(Broker);

			/* Remove the MsgPort from the public list. */

		RemPort(CxPort);

			/* Remove all pending messages. */

		while(Message = GetMsg(CxPort))
			ReplyMsg(Message);

			/* Delete the MsgPort. */

		DeleteMsgPort(CxPort);

		CxPort = NULL;
		Broker = NULL;
	}
}

	/* GetSeconds(UBYTE *String):
	 *
	 *	Calculates the number of seconds corresponding to
	 *	expressions such as `11:25' or `10'.
	 */

WORD
GetSeconds(UBYTE *String)
{
	WORD i,Seconds;

	for(i = strlen(String) - 1 ; i >= 0 ; i--)
	{
		if(String[i] == ':')
		{
			Seconds = atol(&String[i + 1]);

			String[i] = 0;

			Seconds += 60 * atol(String);

			if(Seconds > 30 * 60)
				Seconds = 30 * 60;

			return(Seconds);
		}
	}

	if((Seconds = atol(String)) > 30 * 60)
		Seconds = 30 * 60;

	return(Seconds);
}

	/* SetupCx(UBYTE **ToolTypes):
	 *
	 *	Set up the Commodities interface.
	 */

BYTE __regargs
SetupCx(UBYTE **ToolTypes)
{
		/* Cancel any previously made assignments. */

	ShutdownCx();

		/* Create a reply port. */

	if(CxPort = CreateMsgPort())
	{
			/* Fill in a unique name. */

		CxPort -> mp_Node . ln_Name = NewBroker . nb_Name;

			/* Add the reply port to the public list. */

		AddPort(CxPort);

			/* Install the replyport. */

		NewBroker . nb_Port = CxPort;

			/* Set the Commodity priority if possible. */

		if(ToolTypes)
			NewBroker . nb_Pri  = ArgInt(ToolTypes,"CX_PRIORITY",0);

			/* Create the broker. */

		if(Broker = CxBroker(&NewBroker,NULL))
		{
			CxObj	*ObjectList;
			UBYTE	*String,Buffer[256];

			if(ToolTypes)
			{
					/* Set the Commodity popup hotkey if possible. */

				String = ArgString(ToolTypes,"CX_POPKEY","shift f1");

				strcpy(HotkeyBuffer,String);

					/* Determine the screen blanker hotkey. */

				String = ArgString(ToolTypes,"BLANKSCREEN","shift f2");

				strcpy(BlankScreenBuffer,String);

					/* Cycle the multicolour palette? */

				String = ArgString(ToolTypes,"CYCLE","yes");

				if(!strcmpi(String,"yes"))
					Cycle = TRUE;
				else
					Cycle = FALSE;

					/* Run in monochrome mode? */

				String = ArgString(ToolTypes,"MONO","no");

				if(!strcmpi(String,"yes"))
					Mono = TRUE;
				else
					Mono = FALSE;

					/* Adjust the screen timeout if possible. */

				strcpy(Buffer,ArgString(ToolTypes,"SCREENTIMEOUT","1:00"));

				ScreenTimeout = GetSeconds(Buffer);

					/* Adjust the pattern change timeout if possible. */

				strcpy(Buffer,ArgString(ToolTypes,"PATTERNTIMEOUT","1:00"));

				PatternTimeout = GetSeconds(Buffer);
			}

				/* Link the hotkey. */

			AttachCxObj(Broker,HotKey(HotkeyBuffer,CxPort,POP_WINDOW));

				/* Link another hotkey. */

			AttachCxObj(Broker,HotKey(BlankScreenBuffer,CxPort,BLANK_SCREEN));

				/* Install the plain InputEvent handler. */

			ObjectList = CxCustom(BlankerAction,NULL);

				/* Any accumulated errors? */

			if(!CxObjError(ObjectList))
			{
					/* Add the custom object. */

				AttachCxObj(Broker,ObjectList);

					/* Any errors? */

				if(!CxObjError(Broker))
				{
						/* Activate the broker. */

					ActivateCxObj(Broker,TRUE);

					return(TRUE);
				}
			}
		}
	}

	ShutdownCx();

	return(FALSE);
}

	/* HandleCxMsg(CxMsg *Message):
	 *
	 *	Handle incoming Commodities messages.
	 */

VOID __regargs
HandleCxMsg(CxMsg *Message)
{
	ULONG MessageID = CxMsgID(Message),MessageType = CxMsgType(Message);

	ReplyMsg((struct Message *)Message);

		/* Take a look at the message type. */

	switch(MessageType)
	{
			/* It's a hotkey. */

		case CXM_IEVENT:	switch(MessageID)
					{
							/* Create the control panel. */

						case POP_WINDOW:	SetupWindow();
									break;

							/* Blank the screen. */

						case BLANK_SCREEN:	if(!BlankTask)
										StartBlanker();
									else
									{
											/* Tell the blanker task to change the pattern. */

										Signal(BlankTask,SIGBREAKF_CTRL_D);

										PatternCount = 0;
									}

									break;
					}

					break;

			/* It's an internal Commodities command. */

		case CXM_COMMAND:	switch(MessageID)
					{
							/* Disable the Commodity. */

						case CXCMD_DISABLE:	ActivateCxObj(Broker,FALSE);
									break;

							/* Enable the Commodity. */

						case CXCMD_ENABLE:	ActivateCxObj(Broker,TRUE);
									break;

							/* Create the control panel. */

						case CXCMD_APPEAR:
						case CXCMD_UNIQUE:	SetupWindow();
									break;

							/* Close the control panel. */

						case CXCMD_DISAPPEAR:	ShutdownWindow();
									break;

							/* Remove this Commodity. */

						case CXCMD_KILL:	CloseAll(RETURN_OK);
									break;
					}

					break;
	}
}

	/* ShowTime(struct Gadget *SomeGadget,WORD Level):
	 *
	 *	Gadtools support routine, displays the timeouts.
	 */

LONG __saveds __stdargs
ShowTime(struct Gadget *SomeGadget,WORD Level)
{
	STATIC UBYTE Buffer[30];

	if(Level)
		SPrintf(Buffer,"%2ld.%02ld",Level / 60,Level % 60);
	else
		SPrintf(Buffer,"-Off-");

	return((LONG)Buffer);
}

	/* CreateAllGadgets():
	 *
	 *	Gadtools support routine, creates all the gadgets
	 *	required by the control panel.
	 */

struct Gadget *
CreateAllGadgets(struct Gadget **GadgetArray,struct Gadget **GadgetList,APTR VisualInfo,UWORD TopEdge)
{
	struct Gadget		*Gadget;
	struct NewGadget	 NewGadget;
	UWORD			 Counter = 0;

	if(Gadget = CreateContext(GadgetList))
	{
		NewGadget . ng_Width		= 104;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_GadgetText	= "_Screen Timeout      ";
		NewGadget . ng_TextAttr		= &DefaultFont;
		NewGadget . ng_VisualInfo	= VisualInfo;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_Flags		= 0;
		NewGadget . ng_LeftEdge		= (strlen(NewGadget . ng_GadgetText) + 2 - 1) * 8 - 2;
		NewGadget . ng_TopEdge		= 1 + TopEdge;

		GadgetArray[Counter++] = Gadget = CreateGadget(SLIDER_KIND,Gadget,&NewGadget,
			GT_Underscore,		'_',
			GTSL_Min,		0,
			GTSL_Max,		30 * 60,
			GTSL_Level,		ScreenTimeout,
			GTSL_DispFunc,		ShowTime,
			GTSL_LevelFormat,	"%s",
			GTSL_MaxLevelLen,	5,
		TAG_DONE);

		NewGadget . ng_GadgetText	 = "_Pattern Change      ";
		NewGadget . ng_GadgetID		 = Counter;
		NewGadget . ng_TopEdge		+= NewGadget . ng_Height + 1;

		GadgetArray[Counter++] = Gadget = CreateGadget(SLIDER_KIND,Gadget,&NewGadget,
			GT_Underscore,		'_',
			GTSL_Min,		0,
			GTSL_Max,		30 * 60,
			GTSL_Level,		PatternTimeout,
			GTSL_DispFunc,		ShowTime,
			GTSL_LevelFormat,	"%s",
			GTSL_MaxLevelLen,	5,
		TAG_DONE);

		NewGadget . ng_GadgetText	 = "Hot _Key";
		NewGadget . ng_GadgetID		 = Counter;
		NewGadget . ng_TopEdge		+= NewGadget . ng_Height + 2;
		NewGadget . ng_Height		 = 14;

		GadgetArray[Counter++] = Gadget = CreateGadget(STRING_KIND,Gadget,&NewGadget,
			GT_Underscore,	'_',
			GTST_MaxChars,	256,
			GTST_String,	HotkeyBuffer,
		TAG_DONE);

		NewGadget . ng_GadgetText	 = "_Blank Screen";
		NewGadget . ng_GadgetID		 = Counter;
		NewGadget . ng_TopEdge		+= NewGadget . ng_Height + 1;
		NewGadget . ng_Height		 = 14;

		GadgetArray[Counter++] = Gadget = CreateGadget(STRING_KIND,Gadget,&NewGadget,
			GT_Underscore,	'_',
			GTST_MaxChars,	256,
			GTST_String,	BlankScreenBuffer,
		TAG_DONE);

		NewGadget . ng_GadgetText	 = "_Monochrome";
		NewGadget . ng_GadgetID		 = Counter;
		NewGadget . ng_TopEdge		+= NewGadget . ng_Height + 2;
		NewGadget . ng_Height		 = 12;

		GadgetArray[Counter++] = Gadget = CreateGadget(CHECKBOX_KIND,Gadget,&NewGadget,
			GT_Underscore,	'_',
			GTCB_Checked,	Mono,
		TAG_DONE);

		NewGadget . ng_GadgetText	 = "_Cycle";
		NewGadget . ng_GadgetID		 = Counter;
		NewGadget . ng_TopEdge		+= NewGadget . ng_Height + 1;

		GadgetArray[Counter++] = Gadget = CreateGadget(CHECKBOX_KIND,Gadget,&NewGadget,
			GT_Underscore,	'_',
			GTCB_Checked,	Cycle,
		TAG_DONE);

		NewGadget . ng_GadgetText	= "_Hide";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_Flags		= 0;
		NewGadget . ng_LeftEdge		= 6;
		NewGadget . ng_TopEdge		= HEIGHT - 3 - NewGadget . ng_Height;

		GadgetArray[Counter++] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
			GT_Underscore,	'_',
		TAG_DONE);

		NewGadget . ng_GadgetText	= "_Quit";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_LeftEdge		= WIDTH - 6 - NewGadget . ng_Width;

		GadgetArray[Counter++] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
			GT_Underscore,	'_',
		TAG_DONE);
	}

	return(Gadget);
}

	/* ShutdownWindow():
	 *
	 *	Closes the control panel.
	 */

VOID
ShutdownWindow()
{
	if(Window)
	{
		CloseWindow(Window);

		Window = NULL;
	}

	if(GadgetList)
	{
		FreeGadgets(GadgetList);

		GadgetList = NULL;
	}

	if(VisualInfo)
	{
		FreeVisualInfo(VisualInfo);

		VisualInfo = NULL;
	}

	if(DefaultScreen)
	{
		UnlockPubScreen(NULL,DefaultScreen);

		DefaultScreen = NULL;
	}

	if(Topaz)
	{
		CloseFont(Topaz);

		Topaz = NULL;
	}
}

	/* CentreWindow():
	 *
	 *	Adjust left/top coordinates of window to be opened to
	 *	become visible right below the mouse pointer.
	 */

VOID __regargs
CentreWindow(struct Screen *Screen,WORD *LeftEdge,WORD *TopEdge)
{
	(*LeftEdge)	= Screen -> MouseX - (WIDTH >> 1);
	(*TopEdge)	= Screen -> MouseY - (HEIGHT >> 1);

	while((*LeftEdge) + WIDTH > Screen -> Width)
		(*LeftEdge)--;

	while((*LeftEdge) < 0)
		(*LeftEdge)++;

	while((*TopEdge) + HEIGHT > Screen -> Height)
		(*TopEdge)--;

	while((*TopEdge) < 0)
		(*TopEdge)++;
}

	/* SetupWindow():
	 *
	 *	Creates the control panel and disables the screen
	 *	blanker.
	 */

BYTE
SetupWindow()
{
	if(BlankTask)
	{
		struct Task *Task = BlankTask;

		BlankTask = NULL;

		RemTask(Task);
	}

	if(BlankScreen)
	{
		struct Screen *Screen = BlankScreen;

		BlankScreen = NULL;

		ScreenToBack(Screen);

		FreeVPortCopLists(&Screen -> ViewPort);

		RemakeDisplay();

		CloseScreen(Screen);
	}

	if(Window)
		return(TRUE);

	if(Topaz = (struct TextFont *)OpenFont(&DefaultFont))
	{
		if(DefaultScreen = (struct Screen *)LockPubScreen(NULL))
		{
			if(VisualInfo = GetVisualInfo(DefaultScreen,TAG_DONE))
			{
				if(CreateAllGadgets(&GadgetArray[0],&GadgetList,VisualInfo,DefaultScreen -> WBorTop + DefaultScreen -> Font -> ta_YSize + 1))
				{
					WORD Left,Top;

					CentreWindow(DefaultScreen,&Left,&Top);

					if(ZoomData[0] == -1)
					{
						ZoomData[0] = 0;
						ZoomData[1] = 0;
						ZoomData[2] = WIDTH;
						ZoomData[3] = DefaultScreen -> WBorTop + DefaultScreen -> Font -> ta_YSize + 1;
					}

					if(Window = OpenWindowTags(NULL,
						WA_Left,	Left,
						WA_Top,		Top,

						WA_Width,	WIDTH,
						WA_Height,	HEIGHT + DefaultScreen -> Font -> ta_YSize - 8,

						WA_Activate,	TRUE,
						WA_DragBar,	TRUE,
						WA_DepthGadget,	TRUE,
						WA_CloseGadget,	TRUE,
						WA_RMBTrap,	TRUE,
						WA_Zoom,	ZoomData,

						WA_IDCMP,	IDCMP_CLOSEWINDOW | IDCMP_VANILLAKEY | IDCMP_NEWSIZE | SLIDERIDCMP | BUTTONIDCMP,

						WA_Title,	"Fractal Blanker v1.8",
					TAG_DONE))
					{
						SetFont(Window -> RPort,Topaz);

						AddGList(Window,GadgetList,(UWORD)-1,(UWORD)-1,NULL);
						RefreshGList(GadgetList,Window,NULL,(UWORD)-1);
						GT_RefreshWindow(Window,NULL);

						MoveScreen(Window -> WScreen,0,-Window -> WScreen -> TopEdge);
						ScreenToFront(Window -> WScreen);

						UnlockPubScreen(NULL,DefaultScreen);

						DefaultScreen = NULL;

						return(TRUE);
					}
				}
			}
		}
	}

	ShutdownWindow();

	return(FALSE);
}

	/* CloseAll(LONG ReturnCode):
	 *
	 *	Free all resources and exit the program.
	 */

VOID __regargs
CloseAll(LONG ReturnCode)
{
	if(CxBase && IconBase)
	{
		ShutdownCx();

		ArgArrayDone();
	}

	if(BlankTask)
		RemTask(BlankTask);

	if(BlankScreen)
	{
		ScreenToBack(BlankScreen);

		FreeVPortCopLists(&BlankScreen -> ViewPort);

		RemakeDisplay();

		CloseScreen(BlankScreen);

		BlankScreen = NULL;
	}

	ShutdownWindow();

	if(IconBase)
		CloseLibrary(IconBase);

	if(CxBase)
		CloseLibrary(CxBase);

	if(GadToolsBase)
		CloseLibrary(GadToolsBase);

	if(GfxBase)
		CloseLibrary(GfxBase);

	if(IntuitionBase)
		CloseLibrary(IntuitionBase);

	exit(ReturnCode);
}

	/* OpenAll(int argc,char **argv):
	 *
	 *	Open all resources, initialize the colour table and
	 *	create the Commodities interface.
	 */

VOID __regargs
OpenAll(int argc,char **argv)
{
	UBYTE	**ToolTypes,*String;
	WORD	i,c = 0,r = 15,g = 0,b = 0;

		/* Create a table of rainbow colours. */

	for(i = 0 ; i < 16 ; i++)
		Table[c++] = (r << 8) | ((g++) << 4) | b;

	g = 15;
	r--;

	for(i = 0 ; i < 15 ; i++)
		Table[c++] = ((r--) << 8) | (g << 4) | b;

	r = 0;
	g--;
	b++;

	for(i = 0 ; i < 15 ; i++)
		Table[c++] = (r << 8) | ((g--) << 4) | (b++);

	g = 0;
	b = 15;
	r++;

	for(i = 0 ; i < 15 ; i++)
		Table[c++] = ((r++) << 8) | (g << 4) | b;

	r = 15;
	b--;

	for(i = 0 ; i < 14 ; i++)
		Table[c++] = (r << 8) | (g << 4) | (b--);

		/* Open the libraries we need. */

	if(!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",37)))
		CloseAll(RETURN_FAIL + 0);

	if(!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",37)))
		CloseAll(RETURN_FAIL + 1);

	if(!(GadToolsBase = OpenLibrary("gadtools.library",37)))
		CloseAll(RETURN_FAIL + 2);

	if(!(CxBase = OpenLibrary("commodities.library",37)))
		CloseAll(RETURN_FAIL + 3);

	if(!(IconBase = OpenLibrary("icon.library",37)))
		CloseAll(RETURN_FAIL + 4);

		/* Parse the startup arguments. */

	ToolTypes = ArgArrayInit(argc,argv);

		/* Provide default values. */

	strcpy(HotkeyBuffer,		"shift f1");
	strcpy(BlankScreenBuffer,	"shift f2");

		/* Create the commodities interface. */

	if(!SetupCx(ToolTypes))
		CloseAll(RETURN_FAIL + 5);

		/* Pop up the control panel if necessary. */

	if(ToolTypes)
	{
		String = ArgString(ToolTypes,"CX_POPUP","no");

		if(!strcmpi(String,"yes"))
			SetupWindow();
	}
}

	/* main(int argc,char **argv):
	 *
	 *	That's where all the trouble starts.
	 */

VOID __stdargs
main(int argc,char **argv)
{
	ULONG SignalSet;

		/* Open everything we need. */

	OpenAll(argc,argv);

		/* Go into loop waiting for messages. */

	FOREVER
	{
		SignalSet = (1 << CxPort -> mp_SigBit) | SIGBREAKF_CTRL_C;

			/* If the window is still open, wait for
			 * some news.
			 */

		if(Window)
			SignalSet |= (1 << Window -> UserPort -> mp_SigBit);

		SignalSet = Wait(SignalSet);

			/* There are messages pending at the
			 * Commodities reply port.
			 */

		if(SignalSet & (1 << CxPort -> mp_SigBit))
		{
			CxMsg *Message;

			while(Message = (CxMsg *)GetMsg(CxPort))
				HandleCxMsg(Message);
		}

			/* ^E tells the program to quit. */

		if(SignalSet & SIGBREAKF_CTRL_C)
			CloseAll(RETURN_OK);

			/* If the control panel is still open,
			 * check for new messages.
			 */

		if(Window)
		{
			if(SignalSet & (1 << Window -> UserPort -> mp_SigBit))
			{
				struct IntuiMessage	*Massage;
				struct Gadget		*Gadget;
				ULONG			 Class,Code;

				while(Massage = (struct IntuiMessage *)GT_GetIMsg(Window -> UserPort))
				{
					Class	= Massage -> Class;
					Code	= Massage -> Code;
					Gadget	= (struct Gadget *)Massage -> IAddress;

					GT_ReplyIMsg(Massage);

					switch(Class)
					{
							/* Close the window. */

						case IDCMP_CLOSEWINDOW:	ShutdownWindow();
									break;

						case IDCMP_NEWSIZE:	GT_RefreshWindow(Window,NULL);
									break;

							/* Set the slider values. */

						case IDCMP_MOUSEMOVE:	switch(Gadget -> GadgetID)
									{
										case GAD_SCREENTIMEOUT:	ScreenCount = 0;
													ScreenTimeout = Code;
													break;

										case GAD_PATTERNCHANGE:	PatternCount = 0;
													PatternTimeout = Code;
													break;
									}

									break;

							/* Handle the keyboard shortcuts. */

						case IDCMP_VANILLAKEY:	switch(toupper(Code))
									{
										case 'S':	ScreenCount = 0;

												if(Code == 's')
												{
													if(ScreenTimeout + 1 <= 30 * 60)
														ScreenTimeout++;
													else
														ScreenTimeout = ScreenTimeout + 1 - 30 * 60;
												}
												else
												{
													if(ScreenTimeout > 0)
														ScreenTimeout--;
													else
														ScreenTimeout = 30 * 60;
												}

												GT_SetGadgetAttrs(GadgetArray[GAD_SCREENTIMEOUT],Window,NULL,
													GTSL_Level,ScreenTimeout,
												TAG_DONE);

												break;

										case 'P':	PatternCount = 0;

												if(Code == 'p')
												{
													if(PatternTimeout + 1 <= 30 * 60)
														PatternTimeout++;
													else
														PatternTimeout = PatternTimeout + 1 - 30 * 60;
												}
												else
												{
													if(PatternTimeout > 0)
														PatternTimeout--;
													else
														PatternTimeout = 30 * 60;
												}

												GT_SetGadgetAttrs(GadgetArray[GAD_PATTERNCHANGE],Window,NULL,
													GTSL_Level,PatternTimeout,
												TAG_DONE);

												break;

										case 'K':	ActivateGadget(GadgetArray[GAD_HOTKEY],Window,NULL);
												break;

										case 'B':	ActivateGadget(GadgetArray[GAD_BLANKSCREEN],Window,NULL);
												break;

										case 'M':	Mono ^= TRUE;

												GT_SetGadgetAttrs(GadgetArray[GAD_MONO],Window,NULL,
													GTCB_Checked,Mono,
												TAG_DONE);

												break;

										case 'C':	Cycle ^= TRUE;

												GT_SetGadgetAttrs(GadgetArray[GAD_CYCLE],Window,NULL,
													GTCB_Checked,Cycle,
												TAG_DONE);

												break;

										case 'H':	ShutdownWindow();
												break;

										case 'Q':	CloseAll(RETURN_OK);

										default:	break;
									}

									break;

							/* Handle the gadgets themselves. */

						case IDCMP_GADGETUP:	switch(Gadget -> GadgetID)
									{
										case GAD_HOTKEY:	strcpy(HotkeyBuffer,((struct StringInfo *)Gadget -> SpecialInfo) -> Buffer);

													if(!SetupCx(NULL))
														CloseAll(RETURN_FAIL + 5);

													break;

										case GAD_BLANKSCREEN:	strcpy(BlankScreenBuffer,((struct StringInfo *)Gadget -> SpecialInfo) -> Buffer);

													if(!SetupCx(NULL))
														CloseAll(RETURN_FAIL + 5);

													break;

										case GAD_MONO:		if(Gadget -> Flags & GFLG_SELECTED)
														Mono = TRUE;
													else
														Mono = FALSE;

													break;

										case GAD_CYCLE:		if(Gadget -> Flags & GFLG_SELECTED)
														Cycle = TRUE;
													else
														Cycle = FALSE;

													break;

										case GAD_HIDE:		ShutdownWindow();
													break;

										case GAD_QUIT:		CloseAll(RETURN_OK);
									}

									break;
					}

						/* Window has been closed, do not
						 * continue requesting messages
						 * from the UserPort.
						 */

					if(!Window)
						break;
				}
			}
		}
	}
}