/* MemGauge.c ***************************************************************
*
*	MemGauge - The main program handling initializations and
*	           display.
*
*	Author...  Olaf 'Olsen' Barthel, ED Electronic Design Hannover
*	           Brabeckstrasse 35
*	           D-3000 Hannover 71
*
*	           Federal Republic of Germany
*
*****************************************************************************
*
*	MemGauge can be invoked via Workbench or from CLI. If you
*	chose the latter you may add up to three arguments defining
*	the location the window will be opened at.
*
*	For example: MemGauge 624 11 179
*
****************************************************************************/

#include <intuition/intuitionbase.h>
#include <graphics/gfxbase.h>
#include <exec/execbase.h>
#include <exec/memory.h>

	/* This will send a message to a DOS-stream. */

#define Send(Stream,String)	Write(Stream,String,strlen(String))

	/* Some forward declarations. */

extern struct Library	*OpenLibrary();
extern struct Window	*OpenWindow();
extern struct MenuItem	*ItemAddress();
extern struct Message	*GetMsg();
extern ULONG		AvailMem();

	/* Timer device control. */

extern void		CloseTimerDevice();
extern BOOL		OpenTimerDevice();
extern void		WaitTime();

	/* The following variables are imported from the various
	 * link-modules.
	 */

extern struct ExecBase	*SysBase;
extern struct Menu	GaugeMenu;
extern struct IntuiText	AboutTxt[];
extern struct IntuiText	Proceed;

	/* The menunumber. */

#define GAUGE 0

	/* How often will we check the window for incoming
	 * messages?
	 */

#define TIMEFRAG 8

	/* Some libraries... */

struct IntuitionBase	*IntuitionBase;
struct GfxBase		*GfxBase;

	/* Window and related structures. */

struct Window		*Window;
struct IntuiMessage	*Massage;
struct RastPort		*RPort;

	/* This is what we will get from the window. */

ULONG  Class;
USHORT Code;

	/* To replace the dragbar and to add some comfort
	 * we use this gadget to allow the user to drag the
	 * window around at any location.
	 */

struct Gadget DragGadget =
{
	(struct Gadget *)NULL,
	0,0,
	16,512,
	GADGHNONE,
	GADGIMMEDIATE,
	WDRAGGING,
	(APTR)NULL,
	(APTR)NULL,
	(struct IntuiText *)NULL,
	0,
	(APTR)NULL,
	0,
	(APTR)NULL
};

	/* We will open this window. */

struct NewWindow NewWindow =
{
	0,0,
	16,0,
	2,1,
	REFRESHWINDOW | NEWSIZE | ACTIVEWINDOW | INACTIVEWINDOW | MENUPICK,
	WINDOWSIZING,
	(struct Gadget *)&DragGadget,
	(struct Image  *)NULL,
	(UBYTE *)NULL,
	(struct Screen *)NULL,
	(struct BitMap *)NULL,
	16,64,
	16,512,
	WBENCHSCREEN
};
	/* Length of the block to draw. */

short BarLength = 0;

	/* Available memory and used memory. */

ULONG MaxMem,CurMem;

	/* Bitmapdata for the "E" (Empty) symbol. */

USHORT EptyData[16] =
{
	/* Plane 0 */

	0xC003,0xC7E3,0xC603,0xC783,
	0xC603,0xC7E3,0xC003,0xFFFF,

	/* Plane 1 */

	0x3FFC,0x381C,0x39FC,0x387C,
	0x39FC,0x381C,0x3FFC,0x0000
};

	/* Bitmapdata for the"F" (Full) symbol. */

USHORT FullData[16] =
{
	/* Plane 0 */

	0xFFFF,0xC003,0xC7E3,0xC603,
	0xC783,0xC603,0xC603,0xC003,

	/* Plane 1 */

	0x0000,0x3FFC,0x381C,0x39FC,
	0x387C,0x39FC,0x39FC,0x3FFC
};

	/* Imagedefinitions for "Empty". */

struct Image EptyImage =
{
	0,0,
	16,8,2,
	(USHORT *)&EptyData,
	0x03,0x00,
	(struct Image *)NULL
};

	/* Imagedefinitions for "Full". */

struct Image FullImage =
{
	0,0,
	16,8,2,
	(USHORT *)&FullData,
	0x03,0x00,
	(struct Image *)NULL
};

	/* Some definitions for the detach functions supported
	 * by Aztec and Lattice. Lattice needs some more data to
	 * send a text to stdout, that's why _Backstdout is part
	 * of this code.
	 */

#ifndef AZTEC_C
extern long _Backstdout;
#endif AZTEC_C

long  _stack        = 4000;
long  _priority     = 0;
char *_procname     = "MemGauge v1.4";

	/* You will have to know if you need it: output to the
	 * current CLI console. If you are using Aztec C
	 * this will prevent you from closing the CLI window
	 * MemGauge was started from. Lattice is a bit more
	 * tolerant since you will have to close stdout on your
	 * own. To avoid conflicts this variable (_BackGroundIO)
	 * has been set to 0.
	 */

long  _BackGroundIO = 0;

	/* CloseAll(ReturnCode) :
	 *
	 *	Closes anything and exits.
	 */

void
CloseAll(ReturnCode)
register short ReturnCode;
{
	if(Window)
	{
		while(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
			ReplyMsg(Massage);

		ClearMenuStrip(Window);

		CloseWindow(Window);
	}

	if(IntuitionBase)
		CloseLibrary(IntuitionBase);

	if(GfxBase)
		CloseLibrary(GfxBase);

	CloseTimerDevice();

	exit(ReturnCode);
}

	/* OpenAll() :
	 *
	 *	Opens everything we need.
	 */

void
OpenAll()
{
	if(!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",LIBRARY_VERSION)))
		CloseAll(20);

	if(!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",LIBRARY_VERSION)))
		CloseAll(20);

	if(!(Window = (struct Window *)OpenWindow(&NewWindow)))
		CloseAll(20);

	if(!SetMenuStrip(Window,&GaugeMenu))
		CloseAll(20);

	if(!OpenTimerDevice())
		CloseAll(20);

	RPort = Window -> RPort;
}

	/* MaxMemSize(MemType) :
	 *
	 *	This one was borrowed from GfxMem 0.4 by Louis A.
	 *	Mamakos. MaxMemSize() returns the size of a block
	 *	of memory in the MemList structures to be found
	 *	in ExecBase.
	 */

ULONG
MaxMemSize(MemType)
register unsigned long MemType;
{
	register ULONG BlockSize = 0;
	register struct MemHeader *MemHeader;
	register struct ExecBase *ExecBase = SysBase;

	Forbid();

	for(MemHeader = (struct MemHeader *)ExecBase -> MemList . lh_Head ; MemHeader -> mh_Node . ln_Succ ; MemHeader = (struct MemHeader *)MemHeader -> mh_Node . ln_Succ)
		if(MemHeader -> mh_Attributes & MemType)
			BlockSize += ((ULONG)MemHeader -> mh_Upper - (ULONG)MemHeader -> mh_Lower);

	Permit();

	return(BlockSize);
}

	/* RedrawBar(ReallyDoIt) :
	 *
	 *	Displays the memory usage according to the size
	 *	of our window.
	 */

void
RedrawBar(ReallyDoIt)
BOOL ReallyDoIt;
{
	register short Length;

	static ULONG LastCurMem = 0;

	CurMem = AvailMem(MEMF_CHIP) + AvailMem(MEMF_FAST);
	Length = ((Window -> Height - 16) * CurMem) / MaxMem;

		/* If we don't really need to redraw the graph
		 * we return the compliment.
		 */

	if(CurMem == LastCurMem && !ReallyDoIt)
		return;

		/* Draw occupied memory... */

	SetAPen(RPort,3);
	RectFill(RPort,2,8 + Length,13,Window -> Height - 9);

		/* Add the empty rest. */

	SetAPen(RPort,2);
	RectFill(RPort,2,8,13,8 + Length);

		/* Draw the images. */

	DrawImage(RPort,&FullImage,0,0);
	DrawImage(RPort,&EptyImage,0,Window -> Height - 8);

	LastCurMem = CurMem;
}

	/* This will save some memory. */

void _wb_parse(){}
void MemCleanup(){}

	/* main(argc,argv) :
	 *
	 *	This is where it starts.
	 */

void
main(argc,argv)
long argc;
char *argv[];
{
		/* Timer delay and number of runs */

	short MaxSteps = 1,Steps = TIMEFRAG * MaxSteps;

		/* Some working data for the menu stuff. */

	USHORT MenuNum;
	struct MenuItem *Item;

		/* Position and height of the window. */

	SHORT LeftEdge = 0,TopEdge = 11,Height = 189;

		/* User wants help? */

	if(argv[1][0] == '?')
	{
#ifdef AZTEC_C
		if(_BackGroundIO)
		{
			Send(Output(),"\n[33m[1mMemGauge[0m[31m v1.4 © 1989 by Olaf 'Olsen' Barthel & ED Electronic Design Hannover\n\n");

			Send(Output(),"Useage: MEMGAUGE <[33mLeft edge[31m> <[33mTop edge[31m> <[33mHeight[31m>\n");
		}
#else
		Send(_Backstdout,"\n[33m[1mMemGauge[0m[31m v1.4 © 1989 by Olaf 'Olsen' Barthel & ED Electronic Design Hannover\n\n");

		Send(_Backstdout,"Useage: MEMGAUGE <[33mLeft edge[31m> <[33mTop edge[31m> <[33mHeight[31m>\n");
#endif AZTEC_C
		exit(0);
	}

#ifndef AZTEC_C

		/* Lattice 'C' needs some more lines to get rid of
		 * stdout.
		 */

	if(_Backstdout)
		Close(_Backstdout);

	_Backstdout = 0;

#endif AZTEC_C

		/* Adjust X-position... */

	if(argc > 1)
	{
		LeftEdge = atoi(argv[1]);

		if(LeftEdge < 0)
			LeftEdge = 0;

		if(LeftEdge > 624)
			LeftEdge = 624;
	}

		/* Adjust Y-position... */

	if(argc > 2)
		if((TopEdge = atoi(argv[2])) < 0)
			Height = 0;

		/* Adjust dimensions... */

	if(argc > 3)
		if((Height = atoi(argv[3])) < 64)
			Height = 64;

	NewWindow . LeftEdge	= LeftEdge;
	NewWindow . TopEdge	= TopEdge;
	NewWindow . Height	= Height;

		/* Say what we need. */

	OpenAll();

		/* How much memory is there in this Amiga? */

	MaxMem = MaxMemSize(MEMF_CHIP) + MaxMemSize(MEMF_FAST);

		/* The BIG loop. */

	FOREVER
	{
			/* Wait a while... */

		WaitTime(0,1000000 / TIMEFRAG);

			/* Check window for messages. */

		if(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
		{
			Class	= Massage -> Class;
			Code	= Massage -> Code;

			ReplyMsg(Massage);

				/* Are we to redraw the window? */

			if(Class == NEWSIZE || Class == ACTIVEWINDOW || Class == INACTIVEWINDOW)
			{
				Steps = 0;
				RedrawBar(TRUE);
			}

				/* Intuition wants us to redraw the
				 * window.
				 */

			if(Class == REFRESHWINDOW)
			{
				BeginRefresh(Window);

				Steps = 0;
				RedrawBar(TRUE);

				EndRefresh(Window,TRUE);
			}

				/* User selected a menu item. */

			if(Class == MENUPICK)
			{
				MenuNum = Code;

				while(MenuNum != MENUNULL)
				{
					if(MENUNUM(MenuNum) == GAUGE)
					{
						switch(ITEMNUM(MenuNum))
						{
								/* About... */

							case 0:	AutoRequest(NULL,&AboutTxt[0],NULL,&Proceed,NULL,NULL,274,102);
								break;

								/* Window to front? */

							case 2:	WindowToFront(Window);
								break;

								/* Window to back? */

							case 3:	WindowToBack(Window);
								break;

								/* Close window? */

							case 5: CloseAll(0);

								/* 1 Second update? */

							case 7:	MaxSteps = 1;
								break;

								/* 2 Seconds update? */

							case 8:	MaxSteps = 2;
								break;

								/* 5 Seconds update? */

							case 9:	MaxSteps = 5;
								break;

								/* 10 Seconds update? */

							case 10:MaxSteps = 10;
								break;

							default:break;
						}
					}

						/* Check for next entry. */

					Item = (struct MenuItem *)ItemAddress(&GaugeMenu,MenuNum);

					MenuNum = Item -> NextSelect;
				}
			}
		}

			/* Are we to redraw the window? */

		if((Steps++) < TIMEFRAG * MaxSteps)
			continue;

			/* Reset the counter. */

		Steps = 0;

			/* Redraw the window. */

		RedrawBar(FALSE);
	}
}
