/* -*- Mode:C; Tab-width:4 -*- */
/* TBI CATAGORY A                                                        */
/*                                                                       */
/*                      RESTRICTED RIGHTS LEGEND                         */
/*                                                                       */
/* Use, duplication, or disclosure by the Government is subject to       */
/* restrictions as set forth in subdivision (c)(1)(ii) of the Rights in  */
/* Technical Data and Computer Software clause at 252.227-7013.          */
/*                                                                       */
/*                    TEXAS INSTRUMENTS INCORPORATED.                    */
/*                            P.O. BOX 149149                            */
/*                         AUSTIN, TEXAS 78714-9149                      */
/*                              MS 2151                                  */
/*                                                                       */
/*  Copyright (C) 1987,1988,1989,1990 Texas Instruments Incorporated.    */
/*  All rights reserved.                                                 */
/*                                                                       */

/*	Make sure all the color information is included in the compile.	*/

#define	__ALLNU__

#include <quickdraw.h>
#include <devices.h>
#include <fonts.h>
#include <events.h>
#include <windows.h>
#include <osutils.h>
#include <menus.h>
#include <textedit.h>
#include <dialogs.h>
#include <toolutils.h>
#include <desk.h>
#include <stdio.h>
#include <Micronet-device.h>
#include <Micronet-accessors.h>
#include <errors.h>
#include <serial.h>
#include <strings.h>
#include <addinComm.h>
#include <resources.h>
#include "TbServer.h"
#include <traps.h>
#include <segload.h>
#include <files.h>
#include <osevents.h>   /* 9/6/89 clm - needed for FlushEvents  */

/* 01/17/90 clm - new includes for Comm Toolbox */
#include <CRMIntf.h>
#include <CRMSerialIntf.h>
#include <CMIntf.h>
#include <FTIntf.h>
#include <TMIntf.h>
#include <CTBUtils.h>

/*
	A copy of the string returned by lisp when it gets an error launching an
	application.
*/

char lisp_error_string[256];


/*	Some handy macros.	*/

/* 9/26/89 clm - this is used as an arg to SetApplLimit, which calls for an arg of type Ptr. */
#define StackBase() (Ptr) *((unsigned long *) 0x908) 

#define currtime() *((unsigned long *) 0x16A)


/*	Opcode on the misc channel for starting mac applications on the mx.	*/

#define TOOLBOX_LAUNCH 15
#define nupi_register 0xff000


static acb *MacStateAcb;


Boolean inForeground = true;

/*
	The word offset into which the inForeground flag is stuffed in
	MacStateAcb.
*/

#define inForegroundSlot 13


/*
	The rectangle stack used by the rectangle data type.  This stack is
	used in TBServer.a to move rectangles out of the ACB and into the
	Mac heap.  This gets around a bug in 32 bit Quickdraw where CopyBits
	fails if one of the rectangles passed lies in NuBus memory.
*/

Rect		rectStack[5];
short		rectStackPointer = 0;


/*	0 - Mode				*/
/*	1 - EventAvailMask.		*/

/********************************************************************/
/*																	*/
/*	Application hooks												*/
/*																	*/
/********************************************************************/

/*
	Some dummy functions that define hooks for a user to use when linking in
	the code of the toolbox interface.  They are defined in another segment
	so that we will generate inter segment calls.
*/

#pragma segment User


/*	Initialize any user code. For ex. rpcregister().	*/

void application_init_hook()

{
}


/*	Perform any periodic actions.	*/

void application_run_hook()

{
}


/*
	Preview events before they are shipped off to lisp (only in
	async_event_mode).  Return true if you processed them and do not want
	the toolbox to get them.
*/

/* 04/06/90 sbw:  Removed application_event_hook hook due to removal of async
 * event mode.  Since we no longer remove events from the mac event queue, we
 * cannot pass events to anyone using this hook.  They can call WaitNextEvent 
 * instead.
 *
 * Boolean application_event_hook(event)
 *
 *  EventRecord *event;
 *
 *  {
 * #pragma unused(event)
 *    return false;
 *  }
 */

/*
	Undo anything that needs to be undone. The exit code is passed. 0
	indicates a normal exit.
*/

void application_cleanup_hook(n)
int n;
{
#pragma unused(n)
}

#pragma segment Main

/*	Short term kludge so that micronet-accessors will not complain.	*/

int CPU_slot;

static unsigned long GetEventAvailMask()

{
	EventRecord		event;
	acb				*cmd1;
	Boolean			evtp;
	
	/* ab/may Start change. 6/21/90 */
	if ((unsigned short) parm_16b(MacStateAcb, 12) > 1) {
		/* This is "without interrupt" mode.  Dont give up MAC CPU (except to SystemTask). */
		/* Any non-zero, > 1 value for event-discard flag means "dont give up CPU" for the amount of */
		/* time specified in the timeout parm (parm 2).	*/
		set_parm_32b(MacStateAcb, 2, currtime() + (unsigned long) parm_32b(MacStateAcb, 2));
		while ((unsigned long) currtime() < (unsigned long) parm_32b(MacStateAcb, 2)) {
			handle_micronet_ports();
			if (cmd1 = get_command(trap_channel, true))
				 tb_command(cmd1);
		}
	}   /* ab/may End change. 6/21/90.  Now drop into normal code */
	evtp = EventAvail(-1, &event) ? -1 : 0;
	if (evtp) {
		if (parm_16b(MacStateAcb, 12)) {				/* This is "event discard mode".  Always do WNE to give up CPU and throw away events. */
			WaitNextEvent(everyEvent, &event, 0, 0);
			if (event.what == app4Evt && (event.message >> 24) == 1) {   /* Suspend event */
				inForeground = event.message & activeFlag;
				set_parm_16b(MacStateAcb, inForegroundSlot, inForeground);
			}
		} else if (event.what == app4Evt && (event.message >> 24) == 1) {    /* Here we got a suspend event */
			inForeground = event.message & activeFlag;
			set_parm_32b(MacStateAcb, 3, event.message);
			set_parm_32b(MacStateAcb, 2, currtime() + 60);
			while ((unsigned long) currtime() < (unsigned long) parm_32b(MacStateAcb, 2)) {
				handle_micronet_ports();
				if (cmd1 = get_command(trap_channel, true))
					tb_command(cmd1);
			}
			if (parm_32b(MacStateAcb, 2))
				WaitNextEvent(0, &event, 0, 0);
			set_parm_16b(MacStateAcb, inForegroundSlot, inForeground);
		} else		/* Any event other than suspend.  It is "safe" to do WNE */
			WaitNextEvent(0, &event, 0, 0);
	}
	return evtp;
}

static void UpdateMacStateAcb()

{
	set_parm_32b(MacStateAcb, 1, GetEventAvailMask());
}


/*	0 = Sync events.	*/
/*	1 = async events.	*/
/*	2 = post mask.		*/

unsigned char async_events = 0;


void set_async_event_mode(cmd)

acb *cmd;

{
	async_events = parm_8b(cmd, 0);
	switch (async_events) {
		case 2:
			MacStateAcb = cmd;
			set_deallocatable(MacStateAcb, false);
			break;
		default:
			if (MacStateAcb) {
				set_deallocatable(MacStateAcb, true);
				return_acb_fast(MacStateAcb);
				MacStateAcb = 0;
			}
			break;
	}
}


static void send_event(event)

EventRecord *event;

{
	acb *cmd = get_acb_fast(24);
	set_opcode(cmd, 0);
	set_subopcode(cmd, 0);
	set_parm_16b(cmd, 0, event->what);
	set_parm_32b(cmd, 1, event->message);
	set_parm_32b(cmd, 2, event->when);
	set_parm_16b(cmd, 6, event->where.v);
	set_parm_16b(cmd, 7, event->where.h);
	set_parm_16b(cmd, 8, event->modifiers);
	
/*	Send the current grafport for reference.	*/

	set_parm_32b(cmd, 5, qd.thePort);
	set_requestor_complete(cmd, 1);
	transmit_packet(cmd, trap_channel);
}


/*
	Null events are sent to lisp only if the mouse point or modifiers have
	changed.
*/

Point old_mouse_point; 

unsigned short old_event_modifiers; 

RgnHandle mouse_region;


/*
	The tickcount at which to assume lisp is not going to respong to a
	suspend event.
*/

static int suspend_event_limit_time;

static void increase_suspend_event_limit_time(cmd)

acb *cmd;

{
	suspend_event_limit_time += parm_32b(cmd, 0);
}


/* sbw 03/12/90 mode 2 definition */
int event_handler(event_mask, wait_time)

{
#pragma unused(event_mask)

	EventRecord event;
	
	handle_micronet_ports();
	
	if (async_events == 0)
		WaitNextEvent(0, &event, wait_time, NULL);
	else 
	    UpdateMacStateAcb();
	return event.what; 
}


short GetPixelSize(thePort)

GrafPtr thePort;

{
#pragma unused(thePort)			/* sbw 3/16/90 */

	if ((((CGrafPtr) qd.thePort)->portVersion & 0xC000) == 0xC000) {
		return (**((CGrafPtr) qd.thePort)->portPixMap).pixelSize;
	} else {
		return 1;
	}
}

void SaveMacStateAcb()

{
	Point		pt;
/*	Pattern		penPat; */		/* sbw 03/16/90 unused */
	short		pnPatType, bkPatType, fillPatType;
	RGBColor	rgb;

	GetMouse(&pt);
	
	set_parm_32b(MacStateAcb, 4, qd.thePort);
	set_parm_32b(MacStateAcb, 5, *((unsigned long *) &pt));
	
	if (*(unsigned long *) 0x14C)
		set_parm_32b(MacStateAcb, 1, (unsigned long) 0xFFFFFFFF);
		
	set_parm_32b(MacStateAcb, 7, FrontWindow());
	set_parm_16b(MacStateAcb, 22, ((CGrafPtr) qd.thePort)->portVersion);
	set_parm_16b(MacStateAcb, 23, GetPixelSize(qd.thePort));
	set_parm_16b(MacStateAcb, 24, qd.thePort->portRect.top);
	set_parm_16b(MacStateAcb, 25, qd.thePort->portRect.left);
	set_parm_16b(MacStateAcb, 26, qd.thePort->portRect.bottom);
	set_parm_16b(MacStateAcb, 27, qd.thePort->portRect.right);
	set_parm_32b(MacStateAcb, 15, *((unsigned long *) &qd.thePort->pnLoc));
	set_parm_32b(MacStateAcb, 16, *((unsigned long *) &qd.thePort->pnSize));
	set_parm_16b(MacStateAcb, 34, qd.thePort->pnMode);
	set_parm_16b(MacStateAcb, 35, qd.thePort->txFont);
	set_parm_16b(MacStateAcb, 36, qd.thePort->txFace);
	set_parm_16b(MacStateAcb, 37, qd.thePort->txMode);
	set_parm_16b(MacStateAcb, 38, qd.thePort->txSize);
	if ((((CGrafPtr) qd.thePort)->portVersion & 0xC000) == 0xC000) {
		pnPatType = (**((CGrafPtr) qd.thePort)->pnPixPat).patType;
		set_parm_16b(MacStateAcb, 39, pnPatType);
		if (pnPatType == 0) {
			set_parm_32b(MacStateAcb, 20,
					*(unsigned long *) (&(*(**((CGrafPtr) qd.thePort)->pnPixPat).patData)[0]));
			set_parm_32b(MacStateAcb, 21,
					*(unsigned long *) (&(*(**((CGrafPtr) qd.thePort)->pnPixPat).patData)[4]));
		}
		bkPatType = (**((CGrafPtr) qd.thePort)->bkPixPat).patType;
		set_parm_16b(MacStateAcb, 44, bkPatType);
		if (bkPatType == 0) {
			set_parm_32b(MacStateAcb, 23,
					*(unsigned long *) (&(*(**((CGrafPtr) qd.thePort)->bkPixPat).patData)[0]));
			set_parm_32b(MacStateAcb, 24,
					*(unsigned long *) (&(*(**((CGrafPtr) qd.thePort)->bkPixPat).patData)[4]));
		}
		fillPatType = (**((CGrafPtr) qd.thePort)->fillPixPat).patType;
		set_parm_16b(MacStateAcb, 45, fillPatType);
		if (fillPatType == 0) {
			set_parm_32b(MacStateAcb, 25,
					*(unsigned long *) (&(*(**((CGrafPtr) qd.thePort)->fillPixPat).patData)[0]));
			set_parm_32b(MacStateAcb, 26,
					*(unsigned long *) (&(*(**((CGrafPtr) qd.thePort)->fillPixPat).patData)[4]));
		}
	} else {
		set_parm_16b(MacStateAcb, 39, 0);
		set_parm_32b(MacStateAcb, 20, *(unsigned long *) &qd.thePort->pnPat[0]);
		set_parm_32b(MacStateAcb, 21, *(unsigned long *) &qd.thePort->pnPat[4]);
		set_parm_16b(MacStateAcb, 44, 0);
		set_parm_32b(MacStateAcb, 23, *(unsigned long *) &qd.thePort->bkPat[0]);
		set_parm_32b(MacStateAcb, 24, *(unsigned long *) &qd.thePort->bkPat[4]);
		set_parm_16b(MacStateAcb, 45, 0);
		set_parm_32b(MacStateAcb, 25, *(unsigned long *) &qd.thePort->fillPat[0]);
		set_parm_32b(MacStateAcb, 26, *(unsigned long *) &qd.thePort->fillPat[4]);
	}
	GetForeColor(&rgb);
	set_parm_16b(MacStateAcb, 54, rgb.red);
	set_parm_16b(MacStateAcb, 55, rgb.green);
	set_parm_16b(MacStateAcb, 56, rgb.blue);
	GetBackColor(&rgb);
	set_parm_16b(MacStateAcb, 57, rgb.red);
	set_parm_16b(MacStateAcb, 58, rgb.green);
	set_parm_16b(MacStateAcb, 59, rgb.blue);
}

main()
{
	int	error;
	int CommToolboxTrap = 0x8b;
  	int UnimplementedTrapNumber = 0x9f;


#ifdef MOUSE_REGION

	mouse_region = NewRgn();
	
#endif

	InitGraf(&qd.thePort);
	InitFonts();
	FlushEvents(everyEvent, 0);
	InitWindows();
	InitMenus();
	TEInit();
	InitDialogs(NULL);
	InitCursor();
	
  	/* 01/22/90 clm - new inits for the Comm Toolbox */
  	/* Applies only if the Comm Toolbox is available */
  	/* and running System 6.0 or better...the checks */
  	/* can be added later .... */
  	if (NGetTrapAddress(UnimplementedTrapNumber, OSTrap) != 
     	     NGetTrapAddress(CommToolboxTrap, OSTrap) )
		  	 {
		 	 InitCRM();
          	 InitCTBUtilities();
         	 InitCM();
          	 InitTM();
         	 InitFT();
         	 }

	SetApplLimit(StackBase());
	MaxApplZone();
  
	switch (error = tb_init()) {
		case 0:

/*	Success.	*/

			break;
		
		case NO_APPLICATION_NAME:
			ParamText("There is no lisp application name.","","Exitting to Finder","");
			NoteAlert (260,0);
			exit(-1);
			break;
		
		case NO_TRAP_TABLE:
			ParamText("Error while launching application: ", "Trap resource initialization failed", "Exitting to Finder", "");
			NoteAlert (260,0);
			exit(-1);
			break;

		case TRAP_CHANNEL_OUT_OF_RANGE:
			ParamText("Trap channel out of range.","","Exitting to Finder","");
			NoteAlert (260,0);
			exit(-1);
			break;
		
		default:
			ParamText("Error while launching application: ", lisp_error_string, "Exitting to Finder", "");
			NoteAlert (260,0);
			exit(-1);
			break;

	}

/*	Does not currently return any errors.	*/

	switch (error = rpc_init()) {
		case 0:
			break;
	}

	application_init_hook();
  
/*	Main loop.	*/

	do {
		acb *cmd;
		unsigned long last_time;
		unsigned long start_time = currtime();
		
		handle_micronet_ports();
		
		if (async_events == 2 && MacStateAcb)
			SaveMacStateAcb();
		
		if (cmd = get_command(trap_channel, true)) {
			tb_command(cmd);
			last_time = currtime();
		} else {

/*
	Run for 2 ticks without getting a command or a maximum of 10 ticks.
	This violates the Apple spec for running in the background but we cannot
	tell if we are in the foreground or the background without taking events
	the application program should get.
*/

			unsigned long curr_time = currtime();
			if ((curr_time - last_time > 2) || (curr_time - start_time > 10)) {
				event_handler(nullEvent, 0);
				start_time = currtime();
			}
		}
		application_run_hook();	
	} while (!DriverData->done_flag);

	exit(0);
}


exit(n)

int n;

{
	if (n) {

/*
	6 seconds, time to allow the user to see any messages left on the
	screen.
*/

		int wait_ticks = 100;
		Delay(wait_ticks, &wait_ticks);
	}
	application_cleanup_hook(n);
	tb_shutdown();
	rpc_shutdown();
	ExitToShell();
}


/*	Application support.	*/


/*	Allocate a channel to execute traps and rpc on.						*/
/*	Return Values:		0			- Success.							*/
/*						-1			- No application name				*/
/*						-2			- Trap channel out of range.		*/
/*					>=	-128		- Error returned from lisp.			*/

negotiate_trap_channel()

{
	if (!trap_channel)
		if (((*AppParmHandle)->message) == OUR_LAUNCH_MESSAGE)

/*
	This means that the application was launched from the MX. Get the
	channel number from the finder info.
*/

			trap_channel = (*AppParmHandle)->trap_channel;
		else {

/*
	This means that it was launched from the Mac. Get the lisp application
	name and launch it.  The channel will be the return value.
*/

			char **lisp_application_name;
			short len;
			acb *cmd;
			
			if (!(lisp_application_name = GetResource('NAME', 0)))
				return NO_APPLICATION_NAME;
	
			len = **lisp_application_name;
			cmd = get_acb_fast(256);
			
			set_opcode(cmd, 14);
			set_subopcode(cmd, 0);
			copy_parms_8b(cmd, *lisp_application_name, 0, len + 1, TO_ACB);
			send_command_and_wait(cmd, MISC_CHANNEL);
			
			if (error_code(cmd)) {
				int x;
				char *tmp_ptr = parm_8b_addr(cmd, 0);
	
/*
	Copy the error string returned from lisp into a global for the
	application to see.
*/

				for (x=0; x<256; x++)
					lisp_error_string[x] = tmp_ptr[x];
	
				return error_code(cmd);
			} else			
				trap_channel = parm_8b(cmd, 0);
	
			set_requestor_complete(cmd, 1);
			return_acb_fast(cmd);
		}
	
	
	if ((trap_channel < MIN_TRAP_CHANNEL) || (trap_channel > MAX_TRAP_CHANNEL)) {
		trap_channel = 0;
		return TRAP_CHANNEL_OUT_OF_RANGE;
	}
	
	return 0;
}


Handle trap_table;
Handle trap_index;
unsigned short trap_channel = 0;

void handle_traps(message_from_mx)

acb *message_from_mx;

{

/*	Increment the trap count.	*/

	set_parm_32b(MacStateAcb, 14, parm_32b(MacStateAcb, 14) + 1);

/*	Reset the rectangle stack pointer before calling any trap.	*/

	rectStackPointer = 0;
	traphandler(message_from_mx,trap_table,trap_index);
}


tb_init()

{
	int error;

/*	Turn off the printing of boot progress messages.	*/

	verbose = false;

	if (in_driver_p)
		trap_channel = DRIVER_TRAP_CHANNEL;
	else {
		if (initialize_micronet())
			return MICRONET_INITIALIZE_FAILED;

		if (error = negotiate_trap_channel())
			return error;
	}

	trap_table = GetResource('TRAP', 1);
	trap_index = GetResource('TRAP', 2);

	if (!trap_table || !trap_index)
		return(NO_TRAP_TABLE);
	else {
		MoveHHi(trap_table);
		HLock(trap_table);
		return 0;
	}
}


tb_shutdown()

{
}


extern void socket_command();


void mx_launch(cmd)

acb *cmd;

{
	short error = launch(parm_8b_addr(cmd, 0));
	set_parm_16b(cmd, 0, error);
}


static void rpc_command(pkt)

acb *pkt;

{
	extern int select_word;

	socket_command(pkt);
	if (select_word)
		svc_run();
}


#define MAX_TB_OPCODE 4

static void (*tb_handlers[])(cmd) =
{
	handle_traps,						/* 0  */
	mx_launch,							/* 1  */
	rpc_command,						/* 2  */
	set_async_event_mode,				/* 3  */
	increase_suspend_event_limit_time	/* 4  */	
};


#define COMMAND_NOT_HANDLED 1

void tb_command(cmd)

acb *cmd;

{		
	unsigned char op = opcode(cmd);

	if ((op <= MAX_TB_OPCODE) && tb_handlers[op]) {
		set_error_code(cmd, 0);
		(*tb_handlers[op])(cmd);
	} else

/*	
	Return an error so that Lisp can know that the command was not handled.
*/

		set_error_code(cmd, COMMAND_NOT_HANDLED);

	if (op != 2)
		return_acb_server(cmd);

}


/*	Misc channel opcode for signaling toolbox system errors.	*/

#define TOOLBOX_SIGNAL_ERROR 16

void tb_signal_error(cmd, error)

acb *cmd;
short error;

{
	acb *cmd1 = get_acb(4);
	
	set_opcode(cmd, TOOLBOX_SIGNAL_ERROR);
	set_deallocatable(cmd, false);
	set_error_code(cmd, error);
	set_parm_32b(cmd1, 0, cmd);
	
	send_command_and_wait(cmd1, MISC_CHANNEL);
	set_requestor_complete(cmd1, true);
	return_acb(cmd1);
}


/*
	Read any characters that are available from the serial driver. Also
	returns serial status in ioMisc.
*/

pascal void serial_read(ReadParamBlock)

IOParam *ReadParamBlock;

{
	int result, x, count;
	CntrlParam StatusParamBlock;
	
/*	Find out if there are any characters buffered.	*/

	StatusParamBlock.csCode = 2;
	StatusParamBlock.ioCRefNum = ReadParamBlock->ioRefNum;
	
	if(result = PBStatus((ParmBlkPtr)&StatusParamBlock, false))	{  /* clm 10/30/89 - coerced to ParmBlkPtr */
		DebugStr("\pError sending status call in serial_read");
		ReadParamBlock->ioActCount = 0;
		ReadParamBlock->ioResult = result;
		return;
	}
	    
	if ((count = *((long *) StatusParamBlock.csParam)) == 0) {

/*	There are no characters to read so return.	*/

		ReadParamBlock->ioActCount = 0;
		ReadParamBlock->ioResult = 0;
		return;
	}

/*	Now look for io errors.	*/

	StatusParamBlock.csCode = 8;
	if(result = PBStatus((ParmBlkPtr)&StatusParamBlock, false)) {  /* clm 10/30/89 - coerced to ParmBlkPtr */
		DebugStr("\pError sending status call in serial_read");
		ReadParamBlock->ioActCount = 0;
		ReadParamBlock->ioResult = result;
		return;
	}

/*	Copy the serial status into the memory pointed to by ioMisc.	*/

	for (x=0; x<5; x++)
		*(((unsigned char *)ReadParamBlock->ioMisc) + x) = *(((unsigned char *) StatusParamBlock.csCode) + x);
	if (count < ReadParamBlock->ioReqCount)
		ReadParamBlock->ioReqCount = count;
	if (result = PBRead((ParmBlkPtr)ReadParamBlock, true))  {  /* clm 10/30/89 - coerced to ParmBlkPtr */
		DebugStr("\pError sending read call in serial_read");
		ReadParamBlock->ioActCount = 0;
		ReadParamBlock->ioResult = result;
		return;
	}
	return;
}


/********************************************************************/
/*																	*/
/*	Trap Patches													*/
/*																	*/
/********************************************************************/


struct window_update_region **window_update_regions;


struct window_update_region ** find_window_update_region(window)

WindowRecord *window;

{
	struct window_update_region **wr;
	for (wr = window_update_regions; wr; wr = (*wr)->next)

/*	Found it.	*/

	if ((*wr)->window == window)
		return wr;
	
/*	Didn't find it. We have to allocate one and return it.	*/

	wr = (struct window_update_region **) NewHandle(sizeof(struct window_update_region));
	(*wr)->window = window;
	(*wr)->update_region = NewRgn();
	(*wr)->next = window_update_regions;
	window_update_regions = wr;
	return wr;
}


static void delete_window_update_region(region)

struct window_update_region **region;

{
	struct window_update_region **wr;
	Boolean found_it = false;
 
/*	Delete the first element of the list.	*/

	if (region == window_update_regions) {
		window_update_regions = (*window_update_regions)->next;
		found_it = true;
	} else

/*	Otherwise remove him from somewhere in the middle.	*/

		for (wr = window_update_regions; wr; wr = (*wr)->next)
			if ((*wr)->next == region) {
				(*wr)->next = (*region)->next;
				found_it = true;
				break;
			}
	
/*	Return all of the memory.	*/

	if (found_it) {
		DisposRgn((*region)->update_region);
		DisposHandle((Handle)region);  /* clm 10/30/89 - coerced to Handle */
	} else
		DebugStr("\pDid not find the region in list.");
}


/* sbw 03/12/90 mode 2 definition */
pascal void r_BeginUpdate(window)

WindowRecord *window;

{
	/* struct window_update_region **wr; */	 /* sbw 03/16/90 unused */
	/* GrafPort *port;                   */  /* sbw 03/16/90 unused */
	
	BeginUpdateInternal((WindowPtr)window);  /* clm 10/30/89 - coerced to WindowPtr */
}


struct saved_event
{
	struct saved_event **next;
	EventRecord event;
} **saved_app4_events = 0;


static Boolean get_saved_app4_event(event)

EventRecord *event;

{
	struct saved_event **tmp_event = saved_app4_events;

	if (saved_app4_events) {

/*	Unlink the first one.	*/

		saved_app4_events = (*saved_app4_events)->next;

/*	Copy it.	*/

		*event = (*tmp_event)->event;
		DisposHandle((Handle)tmp_event);  /* clm 10/30/89 - coerced to Handle */
		return true;
	} else
		return false;
}


static void  save_app4_event(event)

EventRecord *event;

{
	struct saved_event **tmp_event, **new_event;
	int x=0;

	if (!(new_event = (struct saved_event **)NewHandle(sizeof(struct saved_event))))
		DebugStr("\pMemory allocation failed.");
	(*new_event)->next = 0;

/*	Copy the event into it.	*/

	(*new_event)->event = *event;
	
	if (!saved_app4_events)
		saved_app4_events = new_event;
	else		  

/*	Find the end of the list and append.	*/

		for (tmp_event = saved_app4_events, x=0; ; tmp_event = (*tmp_event)->next, x++)
			if (!(*tmp_event)->next) {

/*
	Make sure that we do not put an event in the queue more than once.  This
	can happen because WaitNextEvent calls GetNextEvent.
*/

				if ((*tmp_event)->event.when == event->when)
					break;
			  
/*	Thread a new element onto the end.	*/

				(*new_event)->next = (*tmp_event)->next;
				(*tmp_event)->next = new_event;
				break;
			}
	
/*	Keep only the last 6 app4Events received.	*/

	if (x>=6) {
		tmp_event = saved_app4_events;
		saved_app4_events = (*saved_app4_events)->next;
		DisposHandle((Handle)tmp_event);  /* clm 10/30/89 - coerced to Handle */
	}
}


/*
   Note that GetNextEvent is not patched here. In lisp the !GetNextEvent call is routed to !WaitNextEvent and so 
   GetNextEvent is never called. Patching it is a real problem since WaitNextEvent calls GetNextEvent internally.
*/

pascal Boolean r_WaitNextEvent(mask,event,sleep,mouseRgn)

unsigned short mask;
EventRecord *event;
unsigned long sleep;
RgnHandle mouseRgn;

{
	Boolean event_p;

	if ((mask & (unsigned short) app4Mask) && get_saved_app4_event(event))
		return true;

	event_p = WaitNextEventInternal(mask, event, sleep, mouseRgn);

/*
	If we are trying to mask out app4 events then go ahead and return it but
	keep a copy for later.
*/

	if ((event->what == app4Evt) && !(mask & (unsigned short) app4Mask))
		save_app4_event(event);
	
	return event_p;
}


pascal Boolean r_EventAvail(mask, event)

short mask;
EventRecord *event;

{
	Boolean event_p;

	if ((mask & (unsigned short) app4Mask) && get_saved_app4_event(event))
		return true;

	event_p = EventAvailInternal(mask, event);

/*
	If we are trying to mask out app4 events then go ahead and return it but
	keep a copy for later.
*/

	if ((event->what == app4Evt) && !(mask & (unsigned short) app4Mask))
		save_app4_event(event);

	return event_p;
}


void init_trap_patches()

{
	 /* clm 9/27/89 - coerced args to NSetTrapAddress (defined now in OSUtils.h) to be of type long */ 

	BeginUpdateInternalInit();
	NSetTrapAddress((long)r_BeginUpdate, _BeginUpDate, ToolTrap);
	
	WaitNextEventInternalInit();
	NSetTrapAddress((long)r_WaitNextEvent, _WaitNextEvent, ToolTrap);
	
	EventAvailInternalInit();
	NSetTrapAddress((long)r_EventAvail, _EventAvail, ToolTrap);
}


/*
	Polls the trap channel once and calls tb_command if it gets anything.
	This is a function that a user could call to include toolbox and rpc
	service in their application.
*/

void mac_application_run()
{
	extern int svc_fds;

	if (trap_channel) {
		acb *cmd = get_command(trap_channel, true);
		if (cmd)
			tb_command(cmd);
		if (svc_fds & select_word)
			svc_run();
	}
}


int mac_application_init()

{
	int error;
	
	if (error = tb_init())
		return error;
	if (error = rpc_init())
		return error;
	return 0;
}


/****************  old async event mode definitions **************************/

/* 
* int event_handler(event_mask, wait_time)
* 
* {
* 	EventRecord event;
* 	Rect temp_rect;
* 	
* 	handle_micronet_ports();
* 	
* 	if (async_events == 0)
* 		WaitNextEvent(0, &event, wait_time, NULL);
* 	else if (async_events == 2)
* 		UpdateMacStateAcb();
* 	else if (async_events == 1) {
* 		static Boolean suspended_p = false;
* 		RgnHandle update_region;
* 		Boolean send_event_p = false;
* 		WaitNextEvent(-1, &event, 0, 0);
* 		
* 	Only handle the event if the application did not handle it.	
* 
* 		if (!application_event_hook(&event))		
* 			switch (event.what) {				
* 				case  nullEvent:
* 
* 
* 	Only send null events if we are not suspended and the mouse has moved
* 	or the modifiers have changed.
* 
* 
* 					if (!suspended_p && ((old_event_modifiers != event.modifiers) ||
* 							(old_mouse_point.h != event.where.h) ||
* 							(old_mouse_point.v != event.where.v)))
* 						send_event_p = true;
* 					break; 
* 		
* 				case  updateEvt:
* 					{
* 						WindowRecord *window = ((WindowRecord *) event.message);
* 						struct window_update_region **wr = find_window_update_region(window);
* 		
* 	Add to the windows update region.	
* 
* 						UnionRgn(window->updateRgn, (*wr)->update_region, (*wr)->update_region);
* 		
* 
* 	Satisify MultiFinder. This keeps us from getting more update events.
* 
* 
* 						BeginUpdateInternal((WindowRecord *) event.message);				 
* 						EndUpdate((WindowRecord *)event.message);
* 						send_event_p = true;
* 					}
* 					break;
* 				case  app4Evt:
* 					send_event(&event);
* 
* 	Keep track of whether we are suspended or not.	
* 
* 					if ((event.message >> 24) == 1) {
* 						suspended_p = !(event.message & activeFlag);
* 		
* 
* 	We need to make sure that lisp has seen the suspend event before giving
* 	time back to the system.
* 
* 
* 						if (suspended_p) {
* 							acb *cmd1, *cmd = get_acb_fast(0);
* 		
* 	We give them up to 1 second to respond.	
* 
* 							suspend_event_limit_time = currtime() + 60;
* 							set_opcode(cmd, 1);
* 							set_subopcode(cmd, 0);
* 							set_requestor_complete(cmd, false);
* 							set_deallocatable(cmd, false);
* 							transmit_packet(cmd, trap_channel);
* 		
* 
* 	Handle the trap channel while waiting for lisp to respond to the suspend
* 	event.  Note that we cannot call any function to give up time here
* 	because the lisp task may have some actions that must be done before
* 	being suspended (like scrap conversion).
* 
* 
* 							while (!servicer_complete(cmd) && (currtime() < suspend_event_limit_time))
* 								if (cmd1 = get_command(trap_channel, true))
* 									tb_command(cmd1);
* 		
* 							set_requestor_complete(cmd, true);
* 		
* 	Note that there is a small hole here where we can lose an acb.	
* 		
* 							if (servicer_complete(cmd)) {
* 								set_deallocatable(cmd, true);
* 								return_acb_fast(cmd);
* 							} else
* 								set_deallocatable(cmd, true);
* 		
* 						}
* 					}
* 					break;
* 		
* 				case  mouseDown:
* 				case  mouseUp:
* 				case  keyDown:
* 				case  keyUp:
* 				case  autoKey:
* 				case  diskEvt:
* 				case  activateEvt:
* 				case  networkEvt:
* 				case  driverEvt:
* 				case  app1Evt:
* 				case  app2Evt:
* 				case  app3Evt:
* 					send_event_p = true;
* 					break;				
* 			}
* 		
* 		if (send_event_p)
* 			send_event(&event);
* 		
* #ifdef MOUSE_REGION
* 
* 
* 	This code would be very useful if we could post an event when a command
* 	comes in. That way we would not have to depend on null events for
* 	microNet polling.
* 
* 
* 		PT2RECT(event.where, event.where, &temp_rect);
* 		InsetRect(&temp_rect, -1, -1);
* 
* 
* 	Keep up with a region that encloses the mouse so that we can call
* 	WaitNextEvent with a long wait time.
* 
* 
* 		RectRgn(mouse_region , &temp_rect);
* 
* #endif
* 
* 		old_mouse_point.h = event.where.h;
* 		old_mouse_point.v = event.where.v;
* 		old_event_modifiers = event.modifiers;
* 	}
* 	return event.what; 
* }
*/

/*
*	sbw 03/12/90 mode 1 definition
* pascal void r_BeginUpdate(window)
* 
* WindowRecord *window;
* 
* {
* 	struct window_update_region **wr;
* 	GrafPort *port;
* 	
* 	if (async_events == 1) {
* 		wr = find_window_update_region(window);
* 		port = &(window->port);
* 
*  	Add to the windows update region.	 
* 
* 		UnionRgn(window->updateRgn, (*wr)->update_region, window->updateRgn);
* 		delete_window_update_region(wr);
* 	}
* 	BeginUpdateInternal(window);
* }
*/
