/*****************************************************************************
 *
 *								LEACH Ver 1.3
 *
 * This is an auxilery program to Digi-View 2.0. Leach analyses Digi-View's
 * Intuition structures and worms its way in between Intuition and the Host.
 * The purpose of Leach is to provide a "ruler" line that appears on the Host's
 * display screen. See leach.doc for details.
 *
 *            	THIS FILE USES 4 COLUMN TAB STOPS AND 80 COLUMNS
 *
 *****************************************************************************/


#include	<functions.h>
#include	<exec/ports.h>
#include	<exec/types.h>
#include 	<exec/nodes.h>
#include	<exec/libraries.h>
#include	<exec/tasks.h>
#include	<graphics/gfx.h>
#include	<graphics/rastport.h>
#include	<graphics/text.h>
#include	<intuition/intuitionbase.h>
#include 	<stdio.h>


struct Library *GfxBase			= NULL;
struct Library *IntuitionBase	= NULL;


/***********************  SCREEN AND WINDOW STUFF  **************************/

/*--------------------------------------------------------------------------*
 * Leach uses its screen just for the status display window. It displays the
 * ruler on the Host's screen. globals.h declares most of the Host variables.
 *--------------------------------------------------------------------------*/

#define SHOWGAD swind_gad[0]
#define MOVEGAD swind_gad[1]

#define GLOBAL
#include "globals.h"

/************  LEACH'S STATUS DISPLAY NEWSCREEN STRUCTURE  ******************/

static UBYTE ScrTitle[] = " Leach Ver 1.3 ";

struct NewScreen LeachScreen =
{
0, 178,			/* LeftEdge, TopEdge				*/
640, 22, 2,		/* Width, Height, Depth				*/
0, 1,			/* Detail & Block pens.				*/
HIRES,			/* View Modes						*/
CUSTOMSCREEN,	/* Type								*/
NULL,			/* Text attributes					*/
ScrTitle,
NULL,			/* Pointer to screen gadget list.	*/
NULL			/* Pointer to custom bitmap.		*/
};


/******  The rest of this stuff is for Leach's status display window.  ******/

extern struct Gadget	swind_gad[];

static UBYTE StatWindTitle[] = 
" Digi-Leach V1.3                  Copyright (c) 1988 by  Drew Lucy ";

struct NewWindow LeachWindow =
{
0,  0,				/* Upper left corner X & Y		*/
640, 22,			/* Width and Height. Full width, Two lines tall.	*/
0, 1,				/* Detail & Block Pens(UBYTEs)	*/
GADGETUP |			/* IDCMP Flags	(ULONG)			*/
CLOSEWINDOW,
SMART_REFRESH |		/* Window Flags	(ULONG)			*/
WINDOWCLOSE,

swind_gad,			/* Pointer to first gadget.		*/
NULL,				/* Pointer to Checkmark image	*/
StatWindTitle,		/* Pointer to Window Title		*/
NULL,				/* Ptr to Leach's screen		*/
NULL,				/* Pointer to bitmap.			*/
00, 00,				/* Min width & height.			*/
00, 00,				/* Max width & height			*/
CUSTOMSCREEN		/* Screen type for this window	*/
};


/**************************  INTUITEXT STRUCTURE  *****************************/

/*----------------------------------------------------------------------------*
 *	Status display window text. The values of the x's, y's, etc get overwritten
 *	at run time. Any changes to this string must be reflected in the constants
 *	of update_status().
 *----------------------------------------------------------------------------*/

UBYTE stext[] = "X1= 110  Y1= 150  X2= 210  Y2= 150  Len= 0100  Slope= 000.0 ";

struct IntuiText statustext =
{
	1, 0,			/* Front and Back Pens.		*/
	JAM2,			/* Blank out background.	*/
	4, 11,			/* Left edge and Top edge.	*/
	NULL,			/* Use default font.		*/
	&stext[0],		/* Pointer to text string.	*/
	NULL			/* Next IntuiText structure	*/
};


/*****************************  BORDER STRUCTURE  *****************************/

#define XOR COMPLEMENT

/*--------------------------------------------------------------------------*
 * This is the array of ruler screen coordinate pairs. Initialization needs
 * to be kept in sync with the IntuiText string initialization.	Using a 
 * Border struct for one dumb line is probably overkill.
 *--------------------------------------------------------------------------*/

short endpoints[4] = {110, 150, 210, 150};


struct Border ruler =
{
0, 0,		/* LeftEdge & TopEdge offsets.						 */
0, 0,		/* Front and back pens. Neither is used in XOR mode. */
XOR,		/* Draw mode. JAM1 is the other choice.				 */
2,			/* Number of coordinate pairs.						 */
endpoints,	/* Pointer to array of coordinate pairs				 */
NULL		/* Pointer to next Border structure.				 */
};



/*******************************************************************************
 *	Eat shit and die!
 ******************************************************************************/

cleanup()
{

/* Erase ruler, if it is on. */

if ( (SHOWGAD.Flags & SELECTED) && HostRPort )
{
	restore_line();
}
restore_pointer();		/* Make sure Host's mouse pointer is installed.		*/

/*--------------------------------------------------------------------------*
 * If we know our Task address and have installed it in Host's IDCMP...	
 * Restore the original Host IDCMP flags and return control of its UserPort.
 * Note that the general IDCMP flags are reunited with the IDCMP verification
 * flags, which were stored separately. 
 *---------------------------------------------------------------------- ----*/

if (LeachTask && LeachTask == HostUPort->mp_SigTask)
{
	Forbid();
	ModifyIDCMP(HostWind, (HostIDCMPflags | HostVERflags) );
	HostUPort->mp_SigTask = HostTask;
	Permit();
}

if (LeachSig && LeachSig != -(1L) )
	FreeSignal(LeachSig);
	
if (HostWind)
{
	/*----------------------------------------------------------------------*
	 * ReportMouse() doesn't work with Manx C, so turn the flag off directly.
	 * Extract Leach's menu from the Host's menu strip if it exists.
	 *----------------------------------------------------------------------*/

	HostWind->Flags = HostFlags;
	remove_menu();
}
if (MyPort)
{
	myport_event();
	if (MySig)
		FreeSignal( (long)MySig );
	FreeMem(MyPort, (long)sizeof(struct MsgPort) );
}
if (StatWind)		CloseWindow(StatWind);
if (StatScreen)		CloseScreen(StatScreen);
if (IntuitionBase)	CloseLibrary(IntuitionBase);
if (GfxBase)		CloseLibrary(GfxBase);

WBenchToFront();	/* Force WB screen to front in case of error msgs. */

exit();

} /*  End of cleanup()  */


/**************************************************************************
 * Create a MsgPort that the Host can return IntuiMessages to. This port is
 * key to the overall scheme for tapping in to the message traffic between
 * the Host and Intuition. Leach is going to alter the ReplyPort in 
 * messages passed on to the Host so they will be ReplyMsg()'d to this port
 * instead of being returned directly to Intuition.
 **************************************************************************/

create_myport()
{
MyPort = CreatePort("MYPORT", 0L);
if (!MyPort)
{
	puts("Create of MyPort failed.");
	return(FALSE);
}
MySig = MyPort->mp_SigBit;

return(TRUE);
	
} /*  End of create_myport()  */


/**************************************************************************
 *  Routine finds the menu and item numbers of the Host's QUIT menu item
 *	by inspecting the Host's menu list item by item.
 **************************************************************************/

find_quit()
{

static UBYTE		target[] = "QUIT";
UBYTE				menu, item;
UBYTE				found_quit;	/* Is NOT the global variable			 */
struct Menu			*mptr;		/* Working pointer to current menu.		 */
struct MenuItem		*iptr;		/* Working pointer to current menu item. */
UBYTE				*text;		/* Working pointer to menu item text.	 */
short				i;			/* Generic integer.						 */

mptr = HostMenuStrip;
menu = item = 0;
found_quit  = FALSE;

while ( mptr && !found_quit)			/* While more menus...		*/
{
	iptr = mptr->FirstItem;

	while (iptr && !found_quit)			/* While more menu items...	*/
	{
		text = ( (struct IntuiText *)(iptr->ItemFill) )->IText;
		while (*text == ' ')   ++text;	/* Skip leading spaces.	*/

		for (i = 0; (i < 4) && (toupper(text[i]) == target[i]); ++i);
		if (i == 4)
		{
			found_quit  = TRUE;
			H_quit_item = item;
		}
		else
		{
			++item;
			iptr = iptr->NextItem;
		}
	}
	if (found_quit)
	{
		H_quit_menu = menu;
	}
	else
	{
		++menu;
		item = 0;
		mptr = mptr->NextMenu;
	}
}
if (!found_quit)
{
	puts("Can't find Host's QUIT menu item.");
	cleanup(102);
}

return( (int)found_quit );

} /*  End of find_quit()  */
		

/****************************************************************************
 *   This function initializes the color registers for the ViewPort.
 ****************************************************************************/

SetStatScrColors()
{
   SetRGB4(StatVP, 0L,  0L,  0L,  0L);		/* Black */
   SetRGB4(StatVP, 1L,  12L, 12L, 12L);		/* White */
   SetRGB4(StatVP, 2L,  0L,  8L,  12L);
   SetRGB4(StatVP, 3L,  15L, 6L,  0L);

} /*  End of SetStatScrColors()  */


/******************************************************************************
 *	Main Routine
 ******************************************************************************/

main(argc, argv)
int argc;
char *argv[];
{

int		 i;

/*********************  PROCESS COMMAND LINE ARGUMENTS  ********************/

if ( (argv[1][0] == '?') || (argc > 2) )
{
	puts("USAGE: Run Leach after having run a compatible Host program. The");
	puts("       default Host is any of the Digi-View programs. If attaching");
	puts("       Leach to some other Host, specify the Host's screen title");
	puts("       exactly as it appears in the Host's Screen struct and");
	puts("       hopefully on the screen's title bar. For example\n\n");
	puts("               run leach \"Aegis Images V1.1\"\n");
	exit(0);
}


/**********************  OPEN THE SYSTEM LIBRARIES  *************************/

if ( !(GfxBase =  OpenLibrary("graphics.library", 0L) ) )
{
	puts("Can't open Graphics Library.");
	exit();
}
if ( !(IntuitionBase =  OpenLibrary("intuition.library", 0L) ) )
{
	puts("Can't open Intuition Library.");
	cleanup(106);
}


/*************  FIND HOST'S SCREEN AND OTHER VITAL STATISTICS  **************/

Forbid();	/* #################  Shut down multitasking  ################# */

HostTitle  = "Digi-View";
if (argc == 2) HostTitle = argv[1];
HostScreen = ( (struct IntuitionBase *) IntuitionBase )->FirstScreen;

while (HostScreen)
{
	if (HostScreen->DefaultTitle)
	{
		for (i = 0; HostScreen->DefaultTitle[i] == ' '; ++i);

		if ( strcmp(&HostScreen->DefaultTitle[i], HostTitle) )
		{
			HostScreen = HostScreen->NextScreen;
		}
		else	/* It is the Host!	*/
		{
			/* Collect some basic info on the Host.	*/

			HostWind	= HostScreen->FirstWindow;
			HostRPort	= HostWind->RPort; 
			HostUPort	= HostWind->UserPort;
			HostTask	= HostUPort->mp_SigTask;
			HostSig		= HostUPort->mp_SigBit;		/* 0 - 31, not a mask */
			charwidth	= HostRPort->TxWidth;
			charheight	= HostRPort->TxHeight;
			HostFlags	= HostWind->Flags;
			HostMenuStrip  = HostWind->MenuStrip;
			HostIDCMPflags = HostWind->IDCMPFlags;
			HostVERflags   = HostIDCMPflags & (MENUVERIFY | REQVERIFY |
											   SIZEVERIFY);
			HostIDCMPflags &= ~HostVERflags;

			/* HostIDCMPflags now have the Verification flag bits		*/
			/* removed and stored separately in HostVERflags.			*/

			break;			/* Kill while loop  */
		}
	}
	else
	{
		HostScreen = HostScreen->NextScreen;
	}
} /*  end of while (HostScreen)  */

/*--------------------------------------------------------------------------*
 * Bump Leach's task priority to one higher than the Host's. We must be able
 * to preempt the Host immediately, when a msg arrives at the Host's IDCMP.
 *--------------------------------------------------------------------------*/

LeachTask = FindTask(0L); 
LeachTask->tc_Node.ln_Pri = HostTask->tc_Node.ln_Pri + 1;

Permit();	/* #################  Re-enable multitasking  ################# */

if ( !(HostScreen && HostWind && HostRPort && HostUPort && HostTask) )
{
	printf("Some Host parameter is NULL. Is %s running?\n", HostTitle);
	cleanup(108);
}
 
H_found_quit = find_quit();		/* Find the Host's QUIT menu item */


/*--------------------------  Diddle Host's IDCMP  -------------------------*
 *	The objective is to replace the Host's task pointer with one that belongs
 *	to the Leach. There after, Intuition will signal Leach instead of the 
 *	Host when a msg arrives at the Host's UserPort. LeachSig isn't actually
 *	used anywhere in the program. I just wanted to be sure that Leach never
 *	used the signal for anything else. See the file tech-notes for additional
 *	comments on this subject.
 *--------------------------------------------------------------------------*/

LeachSig = AllocSignal( (ULONG)HostSig );	/* Returns a long that's 1-32.	*/

if ( LeachSig != HostSig)
{
	puts("Unable to allocate Leach Signal bit.");
	cleanup(110);
}
HostUPort->mp_SigTask = LeachTask;


/**********************  LAST MINUTE EMERGENCIES  ***********************/

SetStatScrColors();			/* Sets 1st 4 color regs for status screen	*/

/*----------------------------------------------------------------------*
 * Leach wants to know when the Host window becomes active, so it can
 * ignore the mouse click. If the Host had any verification flags set,
 * they were extracted from HostIDCMPflags so, this is also were Host
 * verification will get shut down for the duration.
 *----------------------------------------------------------------------*/
 
HostIDCMPmods = ACTIVEWINDOW;
ModifyIDCMP(HostWind, HostIDCMPflags | HostIDCMPmods);
HostFlagsmods = 0L;

/*--------------------------------------------------------------------------*
 * Initially Leach had trouble working with interlaced Hosts. There was some
 * kind of View/ViewPort conflict, I think. Rather than fix this, I decided
 * to duck the problem by making Leach's screen match the Host's. So, we 
 * check out the Host Screen's height to tell if it's interlaced. This will
 * screw up if not running on an NTSC Amiga.
 *--------------------------------------------------------------------------*/

if (HostScreen->Height > 200)
{
	LeachScreen.ViewModes |= LACE;
	LeachScreen.TopEdge = 400 - LeachScreen.Height;
}


/*****************  OPEN LEACH SCREEN AND STATUS WINDOW  ********************/

if (! ( StatScreen = OpenScreen(&LeachScreen) ) )
{
	puts("Couldn't open Leach's Screen.");
	cleanup(112);
}

/* Only thing StatVP is currently used for is to set the color registers. */

StatVP = &(StatScreen->ViewPort);

LeachWindow.Screen = StatScreen;

if (! ( StatWind = OpenWindow(&LeachWindow) ) )
{
	puts("Couldn't open Leach's window.");
	cleanup(114);
}
StatRPort = StatWind->RPort;
PrintIText(StatRPort, &statustext, 0L, 0L);		/* Initial endpoints	*/
install_menu();
ScreenToFront(HostScreen);

event_processor(); 			/* Currently doesn't return but just in case...	*/

cleanup(116);				/* Rap it up and quit. */

return;

} /*  End of main()  */


/****************************************************************************
 *	A debugging routine that dumps out all the global Host parameters.		*

disp_host_params()
{
printf("Host screen title = %s.\n", HostScreen->DefaultTitle);
printf("Host Screen   @ $%lx. Host Window   @ $%lx.\n", HostScreen, HostWind);
printf("Host UserPort @ $%lx. Host RastPort @ $%lx.\n", HostUPort,  HostRPort);
printf("Host Task     @ $%lx. Host SigBit is   $%x.\n", HostTask,   HostSig);
printf("Leach Task    @ $%lx. Leach SigBit is  $%x.\n", LeachTask,  LeachSig);
printf("Leach priority = %d. Host priority = %d.\n", LeachTask->tc_Node.ln_Pri,
													 HostTask->tc_Node.ln_Pri);
}
****************************************************************************/

