/* $Revision Header * Header built automatically - do not edit! *************
 *
 *	(C) Copyright 1990 by MXM
 *
 *	Name .....: TrackDisplay.c
 *	Created ..: Friday 07-Sep-90 11:03
 *	Revision .: 0
 *
 *	Date            Author          Comment
 *	=========       ========        ====================
 *	07-Sep-90       Olsen           Created this file!
 *
 * $Revision Header ********************************************************/

	/* System includes. */

#include <intuition/intuitionbase.h>
#include <libraries/dosextens.h>
#include <workbench/startup.h>
#include <devices/trackdisk.h>
#include <graphics/gfxbase.h>
#include <hardware/intbits.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <exec/alerts.h>

#define __NO_PRAGMAS 1

#include <functions.h>

	/* Global and shared data. */

extern struct ExecBase	*SysBase;
struct IntuitionBase	*IntuitionBase;
struct GfxBase		*GfxBase;
struct Window		*Window;
struct RastPort		*RPort;

struct Interrupt	*TrackInterrupt;

struct WBStartup	*WBenchMsg;

	/* The four drive IORequests. */

struct IOExtTD		*TrackRequest[4];
struct MsgPort		*TrackPort[4];

	/* Additional track data. */

SHORT			 TrackNumber[4];
SHORT			 TrackData[4];
BYTE			 TrackAvailable[4];

	/* Main process identifier. */

struct Process		*TrackProcess;

	/* Track offset and text spot. */

BYTE			 Offset,Spot;

	/* Default title string. */

char			*TrackString = "DF0: -- DF1: -- DF2: -- DF3: --";

	/* Current directory for segment split. */

BPTR			 RemoteCurrentDir;

	/* Default window, dimensions and position are filled in later. */

struct NewWindow NewWindow =
{
	0,0,
	0,0,
	0,1,
	CLOSEWINDOW | ACTIVEWINDOW | INACTIVEWINDOW,
	RMBTRAP | WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE,
	(struct Gadget *)NULL,
	(struct Image *)NULL,
	(UBYTE *)NULL,
	(struct Screen *)NULL,
	(struct BitMap *)NULL,
	0,0,0,0,
	WBENCHSCREEN
};

	/* TrackHandler():
	 *
	 *	Interrupt routine to check if trackdisk.device has
	 *	moved the head to a different track.
	 */

LONG
TrackHandler()
{
	SHORT	Number;
	SHORT	i,DoSig;

	int_start();

		/* Don't send a refresh signal. */

	DoSig = FALSE;

		/* Check all drives. */

	for(i = 0 ; i < 4 ; i++)
	{
			/* Driver is available. */

		if(TrackAvailable[i])
		{
				/* This line will pick up the current
				 * disk track from the approriate disk
				 * unit. You may say that we are relying
				 * on undefined structures. This is right
				 * for Kickstart 1.3 and below, while
				 * starting with Kickstart 1.4 the current
				 * track indicator has moved into the public
				 * portion of the trackdisk unit.
				 */

			Number = (*(SHORT *)((ULONG)TrackRequest[i] -> iotd_Req . io_Unit + Offset) >> 1);

				/* Has the track number changed? */

			if(TrackNumber[i] == Number)
				TrackData[i] = -1;
			else
			{
					/* It has. Prepare to flag the
					 * main process to update the
					 * window.
					 */

				TrackNumber[i] = TrackData[i] = Number;
				DoSig = TRUE;
			}
		}
		else
			TrackData[i] = -1;
	}

		/* If necessary tell the main process to update the
		 * window.
		 */

	if(DoSig)
		Signal((struct Task *)TrackProcess,SIGBREAKF_CTRL_D);

	int_end();

	return(0);
}

	/* CloseAll():
	 *
	 *	Closes everything we have allocated in order to
	 *	display the tracks.
	 */

VOID
CloseAll()
{
	BYTE i;

		/* Remove and deallocate the interrupt. */

	if(TrackInterrupt)
	{
		RemIntServer(INTB_VERTB,TrackInterrupt);

		FreeMem(TrackInterrupt,sizeof(struct Interrupt));
	}

		/* Free all drives. */

	for(i = 0 ; i < 4 ; i++)
	{
		if(TrackAvailable[i])
			CloseDevice(TrackRequest[i]);

		if(TrackRequest[i])
			DeleteExtIO(TrackRequest[i]);

		if(TrackPort[i])
			DeletePort(TrackPort[i]);
	}

		/* Unlock the remote directory. */

	if(RemoteCurrentDir)
		UnLock(RemoteCurrentDir);

		/* Close the window. */

	if(Window)
		CloseWindow(Window);

		/* Close the libraries. */

	if(GfxBase)
		CloseLibrary(GfxBase);

	if(IntuitionBase)
		CloseLibrary(IntuitionBase);

		/* Return the Workbench Message. */

	if(WBenchMsg)
	{
		Forbid();
		ReplyMsg(&WBenchMsg -> sm_Message);
	}
}

	/* OpenAll():
	 *
	 *	Opens everything we need to display the tracks.
	 */

BYTE
OpenAll()
{
	BYTE i;

	if(!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",LIBRARY_MINIMUM)))
		return(FALSE);

	if(!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",LIBRARY_MINIMUM)))
		return(FALSE);

		/* Check if we are running under control of Exec 2.x. */

	if(SysBase -> LibNode . lib_Version > 34)
	{
		struct Screen WBenchScreen;

			/* The current track is stored in the
			 * 27th word behind the driver unit.
			 */

		Offset = 54;

			/* Adapt the window dimensions to the current
			 * system font.
			 */

		NewWindow . Width = 20 + 24 + 31 * GfxBase -> DefaultFont -> tf_XSize;

		Spot = 24;

		if(!GetScreenData(&WBenchScreen,sizeof(struct Screen),WBENCHSCREEN,NULL))
			return(FALSE);

			/* Center the window. */

		NewWindow . LeftEdge = (WBenchScreen . Width - NewWindow . Width) >> 1;
	}
	else
	{
		struct Screen WBenchScreen;

			/* The current track is stored in the
			 * 37th word behind the driver unit.
			 */

		Offset = 74;

			/* Adapt the window dimensions to the current
			 * system font.
			 */

		NewWindow . Width = 34 + 50 + 31 * GfxBase -> DefaultFont -> tf_XSize;

		Spot = 30;

			/* Get the dimensions of the Workbench screen. */

		if(!GetScreenData(&WBenchScreen,sizeof(struct Screen),WBENCHSCREEN,NULL))
			return(FALSE);

			/* Center the window. */

		NewWindow . LeftEdge = (WBenchScreen . Width - NewWindow . Width) >> 1;

			/* Fill in the default title. */

		NewWindow . Title = (UBYTE *)TrackString;
	}

		/* Adjust the window height. */

	NewWindow . Height = GfxBase -> DefaultFont -> tf_YSize + 2;

		/* Open the window. */

	if(!(Window = (struct Window *)OpenWindow(&NewWindow)))
		return(FALSE);

		/* Open the disk drives. */

	for(i = 0 ; i < 4 ; i++)
	{
		if(!(TrackPort[i] = (struct MsgPort *)CreatePort(NULL,0)))
			return(FALSE);

		if(!(TrackRequest[i] = (struct IOExtTD *)CreateExtIO(TrackPort[i],sizeof(struct IOExtTD))))
			return(FALSE);

		if(!OpenDevice(TD_NAME,i,TrackRequest[i],TDF_ALLOW_NON_3_5))
			TrackAvailable[i] = TRUE;

		TrackNumber[i] = -1;
	}

		/* Allocate memory for the interrupt driver. */

	if(!(TrackInterrupt = (struct Interrupt *)AllocMem(sizeof(struct Interrupt),MEMF_PUBLIC | MEMF_CLEAR)))
		return(FALSE);

	RPort = Window -> RPort;

		/* Set the window colours according to the current
		 * system colours.
		 */

	if(SysBase -> LibNode . lib_Version > 34)
	{
		SetAPen(RPort,1);
		SetBPen(RPort,2);
	}
	else
	{
		SetAPen(RPort,0);
		SetBPen(RPort,1);
	}

	SetDrMd(RPort,JAM2);

		/* Fill in the interrupt. */

	TrackInterrupt -> is_Node . ln_Type	= NT_INTERRUPT;
	TrackInterrupt -> is_Node . ln_Name	= "Track Interrupt";
	TrackInterrupt -> is_Code		= (APTR)TrackHandler;

		/* Add the interrupt server. */

	AddIntServer(INTB_VERTB,TrackInterrupt);

	return(TRUE);
}

	/* _main():
	 *
	 *	The main routine - performs segment split, fires off the
	 *	interrupt and updates the main window.
	 */

LONG
_main()
{
	STATIC char		*ProcName = "Trackdisplay © Copyright 1990 by MXM, all rights reserved";
	STATIC BYTE		 SegmentSplit = FALSE;

	ULONG			 DeadCode = 0xDEADC0DE;

	ULONG			 SignalSet;

	struct IntuiMessage	*Massage;
	ULONG			 Class;

	BYTE			 i;

	TrackProcess = (struct Process *)SysBase -> ThisTask;

		/* Are we running from a CLI? If so, perform
		 * segment split.
		 */

	if(TrackProcess -> pr_CLI)
		SegmentSplit = TRUE;

		/* Pick up the Workbench startup. */

	if(!TrackProcess -> pr_CLI && !SegmentSplit)
	{
		WaitPort(&TrackProcess -> pr_MsgPort);

		WBenchMsg = (struct WBStartup *)GetMsg(&TrackProcess -> pr_MsgPort);

		if(WBenchMsg -> sm_ArgList)
			CurrentDir(WBenchMsg -> sm_ArgList -> wa_Lock);
	}

		/* Are we to do the segment split? */

	if(SegmentSplit)
	{
		struct CommandLineInterface *CLI;

			/* Get a pointer to the current CLI structure. */

		if(CLI = (struct CommandLineInterface *)BADDR(TrackProcess -> pr_CLI))
		{
				/* Make a private copy of the current
				 * directory Lock.
				 */

			CurrentDir(RemoteCurrentDir = CurrentDir(NULL));
			RemoteCurrentDir = DupLock(RemoteCurrentDir);

				/* Create a new process from our
				 * own program code.
				 */

			if(CreateProc(ProcName,0,CLI -> cli_Module,4000))
			{
					/* Keep program segment list
					 * from getting unloaded.
					 */

				CLI -> cli_Module = NULL;

				return(RETURN_OK);
			}
			else
			{
					/* Oops! Process creation failed.
					 * I suppose we're really broken.
					 */

				Alert(AT_Recovery|AG_ProcCreate|AO_Unknown,&DeadCode);

				UnLock(RemoteCurrentDir);

				return(RETURN_FAIL);
			}
		}
		else
		{
				/* At this point we have a copy of the
				 * lock on the current directory of
				 * the creating process. We have lost
				 * the link to the CLI and will need
				 * to transform our segment list in order
				 * to get it unloaded or at least removed
				 * when we are finally falling through.
				 */

			if(!strcmp(TrackProcess -> pr_Task . tc_Node . ln_Name,ProcName))
			{
				struct MemList	*MemList;
				BPTR		*SegList,*LastSeg;
				USHORT		 Count = 0;

					/* Get the pointer to our segment
					 * list.
					 */

				SegList = (BPTR *)BADDR(TrackProcess -> pr_SegList);

					/* The third longword points
					 * to our real segment list, the
					 * first two are DOS-private.
					 */

				SegList = (BPTR *)BADDR(SegList[3]);

					/* Remember the segment list. */

				LastSeg = SegList;

					/* Count the number of segments. */

				while(SegList)
				{
					SegList = (BPTR *)BADDR(*SegList);
					Count++;
				}

					/* Try to allocate a MemList with
					 * enough entries to hold both
					 * the MemList and the segments.
					 */

				if(MemList = (struct MemList *)AllocMem(sizeof(struct MemList) + sizeof(struct MemEntry) * (Count - 1),MEMF_PUBLIC|MEMF_CLEAR))
				{
						/* Restore the segment pointer. */

					SegList = LastSeg;

						/* Set the number of entries. */

					MemList -> ml_NumEntries = Count;

					Count = 0;

						/* Add all the segments to the
						 * memory list.
						 */

					while(SegList)
					{
							/* Start of segment. */

						MemList -> ml_me[Count] . me_Addr	= (APTR)&SegList[-1];

							/* Length of segment. */

						MemList -> ml_me[Count] . me_Length	= SegList[-1];

							/* Get pointer to next segment. */

						SegList = (BPTR *)BADDR(*SegList);

						Count++;
					}

						/* Add the memory list to the list
						 * of memlists to be deallocated
						 * on exit.
						 */

					AddTail(&TrackProcess -> pr_Task . tc_MemEntry,&MemList -> ml_Node);

						/* Change to the last current
						 * directory.
						 */

					CurrentDir(RemoteCurrentDir);
				}
				else
				{
						/* AllocMem failed. We
						 * will show our hand and
						 * try to back out backwards.
						 */

					Alert(AT_Recovery|AG_NoMemory|AO_Unknown,&DeadCode);

					UnLock(RemoteCurrentDir);
					return(RETURN_FAIL);
				}
			}
		}
	}

		/* We're done, now go on running the real program. */

	if(OpenAll())
	{
		FOREVER
		{
				/* Wait for a signal. */

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

				/* Are we to update the window? */

			if(SignalSet & SIGBREAKF_CTRL_D)
			{
					/* Check all drives. */

				for(i = 0 ; i < 4 ; i++)
				{
						/* Is the track valid? */

					if(TrackData[i] != -1)
					{
							/* Put the number into the
							 * string.
							 */

						TrackString[5 + 8 * i + 0] = '0' + TrackData[i] / 10;
						TrackString[5 + 8 * i + 1] = '0' + TrackData[i] % 10;

							/* Print the string. */

						Move(RPort,Spot,GfxBase -> DefaultFont -> tf_Baseline + 1);
						Text(RPort,TrackString,31);
					}
				}
			}

				/* Are we to close the window? */

			if(SignalSet & (1 << Window -> UserPort -> mp_SigBit))
			{
				while(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
				{
					Class = Massage -> Class;

					ReplyMsg((struct Message *)Massage);

						/* Close the window. */

					if(Class == CLOSEWINDOW)
					{
						CloseAll();

						return(RETURN_OK);
					}
					else
					{
							/* Simply refresh
							 * the contents of
							 * the window.
							 */

						Move(RPort,Spot,GfxBase -> DefaultFont -> tf_Baseline + 1);
						Text(RPort,TrackString,31);
					}
				}
			}
		}
	}
}
