/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: bltpatch.cpp,v 1.1.1.1.42.1 2004/07/09 01:59:02 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#if TARGET_CPU_68K && !TARGET_RT_MAC_CFM
	#error Sorry Can't do that
#endif

#include <MixedMode.h>
#include <Appearance.h>
#include <SpeechSynthesis.h>
#include <Traps.h>
#include "macintosh.h"
#include "bltpatch.h"

//#include "../dcondev/dcon.h"

/*

xxxbobclark

Reasons for patches:

Blitting at interrupt time and avoiding menus accounts for the
following trap patches:
- CopyBits (to see where the menus are)
- MenuSelect (to see when the menu is down)
- PopupMenuSelect

Seeing when we switch out of the application accounts for the
following trap patch:
- WaitNextEvent (to watch for osEvt switching in/out)

Ensuring that the current visRgn is what we're using
for the visRgn -- ie, things where we wait for a system-time
call to update our version of the visRgn...
- MoveWindow
- CloseWindow
- SelectWindow
- PaintBehind (like when the app quits)
- BringToFront (deprecated because SelectWindow is better but
  apparently the fine folks at Netscape didn't hear that.)
- SendBehind (also used by Netscape and apparently nobody else)

Note: I think that with the PaintBehind trap being patched, it
makes the CloseWindow patch redundant. At least until code
is added that blits over the last "XORed" part of the dragged
window frame.

Note: Menus popped up from the Control Strip are being blasted
over. ControlStripDispatch is a likely candidate to patch to
turn on the theMenuIsDown boolean, but I'm gonna
procrastinate on that.

*/

#ifdef _CARBON

BlittingPatchInfoStruct gBlitInfo;
bool gRecentUpdateEvent = false;

BOOL gInterruptBlitPatchesInstalled = FALSE;

pascal void PatchBlitSupport(void)
{
}
pascal void UnpatchBlitSupport( void )
{
}

#else

// enums for routinedescriptor
enum {
	upp_CopyBitsProcInfo = kPascalStackBased
		|	STACK_ROUTINE_PARAMETER(1,kFourByteCode)
		|	STACK_ROUTINE_PARAMETER(2,kFourByteCode)
		|	STACK_ROUTINE_PARAMETER(3,kFourByteCode)
		|	STACK_ROUTINE_PARAMETER(4,kFourByteCode)
		|	STACK_ROUTINE_PARAMETER(5,kTwoByteCode)
		|	STACK_ROUTINE_PARAMETER(6,kFourByteCode),
	upp_MenuSelectProcInfo = kPascalStackBased
		|	RESULT_SIZE(kFourByteCode)
		|	STACK_ROUTINE_PARAMETER(1,kFourByteCode),
	upp_PopupMenuSelectProcInfo = kPascalStackBased
		|	RESULT_SIZE(kFourByteCode)
		|	STACK_ROUTINE_PARAMETER(1,kFourByteCode)
		|	STACK_ROUTINE_PARAMETER(2,kTwoByteCode)
		|	STACK_ROUTINE_PARAMETER(3,kTwoByteCode)
		|	STACK_ROUTINE_PARAMETER(4,kTwoByteCode),
	upp_MoveWindowProcInfo = kPascalStackBased
		|	STACK_ROUTINE_PARAMETER(1,kFourByteCode)
		|	STACK_ROUTINE_PARAMETER(2,kTwoByteCode)
		|	STACK_ROUTINE_PARAMETER(3,kTwoByteCode)
		|	STACK_ROUTINE_PARAMETER(4,kOneByteCode),
	upp_WaitNextEventProcInfo = kPascalStackBased
		|	RESULT_SIZE(kOneByteCode)
		|	STACK_ROUTINE_PARAMETER(1,kTwoByteCode)
		|	STACK_ROUTINE_PARAMETER(2,kFourByteCode)
		|	STACK_ROUTINE_PARAMETER(3,kFourByteCode)
		|	STACK_ROUTINE_PARAMETER(4,kFourByteCode),
	upp_CloseWindowProcInfo = kPascalStackBased
		|	STACK_ROUTINE_PARAMETER(1,kFourByteCode),
	upp_SelectWindowProcInfo = kPascalStackBased
		|	STACK_ROUTINE_PARAMETER(1,kFourByteCode),
	upp_PaintBehindProcInfo = kPascalStackBased
		|	STACK_ROUTINE_PARAMETER(1,kFourByteCode)
		|	STACK_ROUTINE_PARAMETER(2,kFourByteCode),
	upp_BringToFrontProcInfo = kPascalStackBased
		|	STACK_ROUTINE_PARAMETER(1,kFourByteCode),
	upp_SendBehindProcInfo = kPascalStackBased
		|	STACK_ROUTINE_PARAMETER(1,kFourByteCode)
		|	STACK_ROUTINE_PARAMETER(2,kFourByteCode),
	uppPatcExtraInfo
};

// Function Prototypes
extern pascal void my_CopyBitsPatch(long param1, long param2, long param3, long param4, short param5, long param6);
extern pascal long my_MenuSelectPatch(long param1);
extern pascal long my_PopupMenuSelectPatch(long param1, short param2, short param3, short param4);
extern pascal void my_MoveWindowPatch(long param1, short param2, short param3, Boolean param4);
extern pascal Boolean my_WaitNextEventPatch(short param1, long param2, long param3, long param4);
extern pascal void my_CloseWindowPatch(long param1);
extern pascal void my_SelectWindowPatch(long param1);
extern pascal void my_PaintBehindPatch(long param1, long param2);
extern pascal void my_BringToFrontPatch(long param1);
extern pascal void my_SendBehindPatch(long param1, long param2);


// Globals to hold original routine descriptors
RoutineDescriptorPtr gOriginal_CopyBits = nil;
RoutineDescriptorPtr gOriginal_MenuSelect = nil;
RoutineDescriptorPtr gOriginal_PopupMenuSelect = nil;
RoutineDescriptorPtr gOriginal_MoveWindow = nil;
RoutineDescriptorPtr gOriginal_WaitNextEvent = nil;
RoutineDescriptorPtr gOriginal_CloseWindow = nil;
RoutineDescriptorPtr gOriginal_SelectWindow = nil;
RoutineDescriptorPtr gOriginal_PaintBehind = nil;
RoutineDescriptorPtr gOriginal_BringToFront = nil;
RoutineDescriptorPtr gOriginal_SendBehind = nil;

#define kMaxMenuRects 20

typedef pascal void (*MenuCallbackProc)( Rect* theRectToRestore );
typedef pascal void (*VisRgnChangedCallbackProc)(void);

BlittingPatchInfoStruct gBlitInfo;
bool gRecentUpdateEvent = false;

BOOL gInterruptBlitPatchesInstalled = FALSE;

// Tell MetroWerks the procInfo for main
ProcInfoType __procinfo =  kPascalStackBased;
pascal void PatchBlitSupport(void)
{
	RoutineDescriptorPtr originaldesc, newdesc, unImplimentedTrapAddr;
	UInt32 size;
	UInt16 index;
	THz theZone;
	int i;

	// detach ourselves
	//DetachResource(Get1Resource('INIT', 0));	 Are we *trying* to crash?
	
    // if the gestalt selector is installed and 
    // the interruptblit bit (low bit) is clear, bail
    
	SInt32 gestResult;
    const OSType kHXCoreGestaltSelector = 'RNc\0';
    const UINT32 kInterruptBlitBit = 0;	// if lowest bit set, we blit at interrupt time

    if (Gestalt(kHXCoreGestaltSelector, &gestResult) == noErr 
    	&& (gestResult & (1L << kInterruptBlitBit)) == 0)
    {
		gInterruptBlitPatchesInstalled = FALSE;
		// DebugStr("\p Bypassing patch installation");
		return;
	}
	
	gInterruptBlitPatchesInstalled = TRUE;
	
	// make sure we are in the system heap
	theZone = GetZone();
	SetZone(SystemZone());
	
	unImplimentedTrapAddr = NGetTrapAddress(_Unimplemented, (_Unimplemented & 0x0800) ? ToolTrap : OSTrap);

	originaldesc = NGetTrapAddress(_CopyBits, (_CopyBits & 0x0800) ? ToolTrap : OSTrap);
	if (originaldesc != unImplimentedTrapAddr) {
		if (originaldesc->goMixedModeTrap != _MixedModeMagic) {
			// Trap is 68K
			size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * 0);
			// allocate new descriptor
			newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
			if (!newdesc) {
				return;
			}
			// set fields of routine decscriptor
			newdesc->goMixedModeTrap = _MixedModeMagic;
			newdesc->version = 7;
			newdesc->routineDescriptorFlags = 0;
			newdesc->reserved1 = 0;
			newdesc->reserved2 = 0;
			newdesc->selectorInfo = 0;
			newdesc->routineCount = 0;
			index = 0;
			gOriginal_CopyBits = originaldesc;
			newdesc->routineRecords[index].procInfo = upp_CopyBitsProcInfo;
			newdesc->routineRecords[index].reserved1 = 0;
			newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
			newdesc->routineRecords[index].routineFlags = 4;
			newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_CopyBitsPatch;
			newdesc->routineRecords[index].reserved2 = 0;
			newdesc->routineRecords[index].selector = 0;
		}
		else {
			// Trap is PPC
			size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * originaldesc->routineCount);
			newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
			if (!newdesc) {
				return;
			}
			BlockMoveData(originaldesc, newdesc, size);
			gOriginal_CopyBits = originaldesc;
			index = 0;
			newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_CopyBitsPatch;
			newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
		}
		NSetTrapAddress((UniversalProcPtr) newdesc, _CopyBits, (_CopyBits & 0x0800) ? ToolTrap : OSTrap);
	}

	originaldesc = NGetTrapAddress(_MenuSelect, (_MenuSelect & 0x0800) ? ToolTrap : OSTrap);
	if (originaldesc != unImplimentedTrapAddr) {
		if (originaldesc->goMixedModeTrap != _MixedModeMagic) {
			// Trap is 68K
			size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * 0);
			// allocate new descriptor
			newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
			if (!newdesc) {
				return;
			}
			// set fields of routine decscriptor
			newdesc->goMixedModeTrap = _MixedModeMagic;
			newdesc->version = 7;
			newdesc->routineDescriptorFlags = 0;
			newdesc->reserved1 = 0;
			newdesc->reserved2 = 0;
			newdesc->selectorInfo = 0;
			newdesc->routineCount = 0;
			index = 0;
			gOriginal_MenuSelect = originaldesc;
			newdesc->routineRecords[index].procInfo = upp_MenuSelectProcInfo;
			newdesc->routineRecords[index].reserved1 = 0;
			newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
			newdesc->routineRecords[index].routineFlags = 4;
			newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_MenuSelectPatch;
			newdesc->routineRecords[index].reserved2 = 0;
			newdesc->routineRecords[index].selector = 0;
		}
		else {
			// Trap is PPC
			size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * originaldesc->routineCount);
			newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
			if (!newdesc) {
				return;
			}
			BlockMoveData(originaldesc, newdesc, size);
			gOriginal_MenuSelect = originaldesc;
			index = 0;
			newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_MenuSelectPatch;
			newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
		}
		NSetTrapAddress((UniversalProcPtr) newdesc, _MenuSelect, (_MenuSelect & 0x0800) ? ToolTrap : OSTrap);
	}

	originaldesc = NGetTrapAddress(_PopUpMenuSelect, (_PopUpMenuSelect & 0x0800) ? ToolTrap : OSTrap);
	if (originaldesc != unImplimentedTrapAddr) {
		if (originaldesc->goMixedModeTrap != _MixedModeMagic) {
			// Trap is 68K
			size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * 0);
			// allocate new descriptor
			newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
			if (!newdesc) {
				return;
			}
			// set fields of routine decscriptor
			newdesc->goMixedModeTrap = _MixedModeMagic;
			newdesc->version = 7;
			newdesc->routineDescriptorFlags = 0;
			newdesc->reserved1 = 0;
			newdesc->reserved2 = 0;
			newdesc->selectorInfo = 0;
			newdesc->routineCount = 0;
			index = 0;
			gOriginal_PopupMenuSelect = originaldesc;
			newdesc->routineRecords[index].procInfo = upp_PopupMenuSelectProcInfo;
			newdesc->routineRecords[index].reserved1 = 0;
			newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
			newdesc->routineRecords[index].routineFlags = 4;
			newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_PopupMenuSelectPatch;
			newdesc->routineRecords[index].reserved2 = 0;
			newdesc->routineRecords[index].selector = 0;
		}
		else {
			// Trap is PPC
			size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * originaldesc->routineCount);
			newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
			if (!newdesc) {
				return;
			}
			BlockMoveData(originaldesc, newdesc, size);
			gOriginal_PopupMenuSelect = originaldesc;
			index = 0;
			newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_PopupMenuSelectPatch;
			newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
		}
		NSetTrapAddress((UniversalProcPtr) newdesc, _PopUpMenuSelect, (_PopUpMenuSelect & 0x0800) ? ToolTrap : OSTrap);
	}

	originaldesc = NGetTrapAddress(_MoveWindow, (_MoveWindow & 0x0800) ? ToolTrap : OSTrap);
	if (originaldesc != unImplimentedTrapAddr) {
		if (originaldesc->goMixedModeTrap != _MixedModeMagic) {
			// Trap is 68K
			size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * 0);
			// allocate new descriptor
			newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
			if (!newdesc) {
				return;
			}
			// set fields of routine decscriptor
			newdesc->goMixedModeTrap = _MixedModeMagic;
			newdesc->version = 7;
			newdesc->routineDescriptorFlags = 0;
			newdesc->reserved1 = 0;
			newdesc->reserved2 = 0;
			newdesc->selectorInfo = 0;
			newdesc->routineCount = 0;
			index = 0;
			gOriginal_MoveWindow = originaldesc;
			newdesc->routineRecords[index].procInfo = upp_MoveWindowProcInfo;
			newdesc->routineRecords[index].reserved1 = 0;
			newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
			newdesc->routineRecords[index].routineFlags = 4;
			newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_MoveWindowPatch;
			newdesc->routineRecords[index].reserved2 = 0;
			newdesc->routineRecords[index].selector = 0;
		}
		else {
			// Trap is PPC
			size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * originaldesc->routineCount);
			newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
			if (!newdesc) {
				return;
			}
			BlockMoveData(originaldesc, newdesc, size);
			gOriginal_MoveWindow = originaldesc;
			index = 0;
			newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_MoveWindowPatch;
			newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
		}
		NSetTrapAddress((UniversalProcPtr) newdesc, _MoveWindow, (_MoveWindow & 0x0800) ? ToolTrap : OSTrap);
	}
	
	originaldesc = NGetTrapAddress(_WaitNextEvent, (_WaitNextEvent & 0x0800) ? ToolTrap : OSTrap);
	if (originaldesc != unImplimentedTrapAddr) {
		// yeesh, forcing it to 68K! Ouchie
		// Trap is 68K
		size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * 0);
		// allocate new descriptor
		newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
		if (!newdesc) {
			return;
		}
		// set fields of routine decscriptor
		newdesc->goMixedModeTrap = _MixedModeMagic;
		newdesc->version = 7;
		newdesc->routineDescriptorFlags = 0;
		newdesc->reserved1 = 0;
		newdesc->reserved2 = 0;
		newdesc->selectorInfo = 0;
		newdesc->routineCount = 0;
		index = 0;
		gOriginal_WaitNextEvent = originaldesc;
		newdesc->routineRecords[index].procInfo = upp_WaitNextEventProcInfo;
		newdesc->routineRecords[index].reserved1 = 0;
		newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
		newdesc->routineRecords[index].routineFlags = 4;
		newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_WaitNextEventPatch;
		newdesc->routineRecords[index].reserved2 = 0;
		newdesc->routineRecords[index].selector = 0;
		
		NSetTrapAddress((UniversalProcPtr) newdesc, _WaitNextEvent, (_WaitNextEvent & 0x0800) ? ToolTrap : OSTrap);
	}

	originaldesc = NGetTrapAddress(_CloseWindow, (_CloseWindow & 0x0800) ? ToolTrap : OSTrap);
	if (originaldesc != unImplimentedTrapAddr) {
		if (originaldesc->goMixedModeTrap != _MixedModeMagic) {
			// Trap is 68K
			size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * 0);
			// allocate new descriptor
			newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
			if (!newdesc) {
				return;
			}
			// set fields of routine decscriptor
			newdesc->goMixedModeTrap = _MixedModeMagic;
			newdesc->version = 7;
			newdesc->routineDescriptorFlags = 0;
			newdesc->reserved1 = 0;
			newdesc->reserved2 = 0;
			newdesc->selectorInfo = 0;
			newdesc->routineCount = 0;
			index = 0;
			gOriginal_CloseWindow = originaldesc;
			newdesc->routineRecords[index].procInfo = upp_CloseWindowProcInfo;
			newdesc->routineRecords[index].reserved1 = 0;
			newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
			newdesc->routineRecords[index].routineFlags = 4;
			newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_CloseWindowPatch;
			newdesc->routineRecords[index].reserved2 = 0;
			newdesc->routineRecords[index].selector = 0;
		}
		else {
			// Trap is PPC
			size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * originaldesc->routineCount);
			newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
			if (!newdesc) {
				return;
			}
			BlockMoveData(originaldesc, newdesc, size);
			gOriginal_CloseWindow = originaldesc;
			index = 0;
			newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_CloseWindowPatch;
			newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
		}
		NSetTrapAddress((UniversalProcPtr) newdesc, _CloseWindow, (_CloseWindow & 0x0800) ? ToolTrap : OSTrap);
	}

	originaldesc = NGetTrapAddress(_SelectWindow, (_SelectWindow & 0x0800) ? ToolTrap : OSTrap);
	if (originaldesc != unImplimentedTrapAddr) {
		if (originaldesc->goMixedModeTrap != _MixedModeMagic) {
			// Trap is 68K
			size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * 0);
			// allocate new descriptor
			newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
			if (!newdesc) {
				return;
			}
			// set fields of routine decscriptor
			newdesc->goMixedModeTrap = _MixedModeMagic;
			newdesc->version = 7;
			newdesc->routineDescriptorFlags = 0;
			newdesc->reserved1 = 0;
			newdesc->reserved2 = 0;
			newdesc->selectorInfo = 0;
			newdesc->routineCount = 0;
			index = 0;
			gOriginal_SelectWindow = originaldesc;
			newdesc->routineRecords[index].procInfo = upp_SelectWindowProcInfo;
			newdesc->routineRecords[index].reserved1 = 0;
			newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
			newdesc->routineRecords[index].routineFlags = 4;
			newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_SelectWindowPatch;
			newdesc->routineRecords[index].reserved2 = 0;
			newdesc->routineRecords[index].selector = 0;
		}
		else {
			// Trap is PPC
			size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * originaldesc->routineCount);
			newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
			if (!newdesc) {
				return;
			}
			BlockMoveData(originaldesc, newdesc, size);
			gOriginal_SelectWindow = originaldesc;
			index = 0;
			newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_SelectWindowPatch;
			newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
		}
		NSetTrapAddress((UniversalProcPtr) newdesc, _SelectWindow, (_SelectWindow & 0x0800) ? ToolTrap : OSTrap);
	}

	originaldesc = NGetTrapAddress(_PaintBehind, (_PaintBehind & 0x0800) ? ToolTrap : OSTrap);
	if (originaldesc != unImplimentedTrapAddr) {
		if (originaldesc->goMixedModeTrap != _MixedModeMagic) {
			// Trap is 68K
			size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * 0);
			// allocate new descriptor
			newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
			if (!newdesc) {
				return;
			}
			// set fields of routine decscriptor
			newdesc->goMixedModeTrap = _MixedModeMagic;
			newdesc->version = 7;
			newdesc->routineDescriptorFlags = 0;
			newdesc->reserved1 = 0;
			newdesc->reserved2 = 0;
			newdesc->selectorInfo = 0;
			newdesc->routineCount = 0;
			index = 0;
			gOriginal_PaintBehind = originaldesc;
			newdesc->routineRecords[index].procInfo = upp_PaintBehindProcInfo;
			newdesc->routineRecords[index].reserved1 = 0;
			newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
			newdesc->routineRecords[index].routineFlags = 4;
			newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_PaintBehindPatch;
			newdesc->routineRecords[index].reserved2 = 0;
			newdesc->routineRecords[index].selector = 0;
		}
		else {
			// Trap is PPC
			size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * originaldesc->routineCount);
			newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
			if (!newdesc) {
				return;
			}
			BlockMoveData(originaldesc, newdesc, size);
			gOriginal_PaintBehind = originaldesc;
			index = 0;
			newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_PaintBehindPatch;
			newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
		}
		NSetTrapAddress((UniversalProcPtr) newdesc, _PaintBehind, (_PaintBehind & 0x0800) ? ToolTrap : OSTrap);
	}

	originaldesc = NGetTrapAddress(_BringToFront, (_BringToFront & 0x0800) ? ToolTrap : OSTrap);
	if (originaldesc != unImplimentedTrapAddr) {
		if (originaldesc->goMixedModeTrap != _MixedModeMagic) {
			// Trap is 68K
			size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * 0);
			// allocate new descriptor
			newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
			if (!newdesc) {
				return;
			}
			// set fields of routine decscriptor
			newdesc->goMixedModeTrap = _MixedModeMagic;
			newdesc->version = 7;
			newdesc->routineDescriptorFlags = 0;
			newdesc->reserved1 = 0;
			newdesc->reserved2 = 0;
			newdesc->selectorInfo = 0;
			newdesc->routineCount = 0;
			index = 0;
			gOriginal_BringToFront = originaldesc;
			newdesc->routineRecords[index].procInfo = upp_BringToFrontProcInfo;
			newdesc->routineRecords[index].reserved1 = 0;
			newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
			newdesc->routineRecords[index].routineFlags = 4;
			newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_BringToFrontPatch;
			newdesc->routineRecords[index].reserved2 = 0;
			newdesc->routineRecords[index].selector = 0;
		}
		else {
			// Trap is PPC
			size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * originaldesc->routineCount);
			newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
			if (!newdesc) {
				return;
			}
			BlockMoveData(originaldesc, newdesc, size);
			gOriginal_BringToFront = originaldesc;
			index = 0;
			newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_BringToFrontPatch;
			newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
		}
		NSetTrapAddress((UniversalProcPtr) newdesc, _BringToFront, (_BringToFront & 0x0800) ? ToolTrap : OSTrap);
	}

	originaldesc = NGetTrapAddress(_SendBehind, (_SendBehind & 0x0800) ? ToolTrap : OSTrap);
	if (originaldesc != unImplimentedTrapAddr) {
		if (originaldesc->goMixedModeTrap != _MixedModeMagic) {
			// Trap is 68K
			size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * 0);
			// allocate new descriptor
			newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
			if (!newdesc) {
				return;
			}
			// set fields of routine decscriptor
			newdesc->goMixedModeTrap = _MixedModeMagic;
			newdesc->version = 7;
			newdesc->routineDescriptorFlags = 0;
			newdesc->reserved1 = 0;
			newdesc->reserved2 = 0;
			newdesc->selectorInfo = 0;
			newdesc->routineCount = 0;
			index = 0;
			gOriginal_SendBehind = originaldesc;
			newdesc->routineRecords[index].procInfo = upp_SendBehindProcInfo;
			newdesc->routineRecords[index].reserved1 = 0;
			newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
			newdesc->routineRecords[index].routineFlags = 4;
			newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_SendBehindPatch;
			newdesc->routineRecords[index].reserved2 = 0;
			newdesc->routineRecords[index].selector = 0;
		}
		else {
			// Trap is PPC
			size = sizeof(RoutineDescriptor) + (sizeof(RoutineRecord) * originaldesc->routineCount);
			newdesc = (RoutineDescriptorPtr) NewPtrSys(size);
			if (!newdesc) {
				return;
			}
			BlockMoveData(originaldesc, newdesc, size);
			gOriginal_SendBehind = originaldesc;
			index = 0;
			newdesc->routineRecords[index].procDescriptor = (ProcPtr)my_SendBehindPatch;
			newdesc->routineRecords[index].ISA = GetCurrentArchitecture();
		}
		NSetTrapAddress((UniversalProcPtr) newdesc, _SendBehind, (_SendBehind & 0x0800) ? ToolTrap : OSTrap);
	}



	
	gBlitInfo.theMenuIsDown = false;
	gBlitInfo.thisApplicationIsFrontmost = true;
	gBlitInfo.clientPSN.highLongOfPSN = 0;
	gBlitInfo.clientPSN.lowLongOfPSN = 0;
	gBlitInfo.MenuUpCallback = nil;
	gBlitInfo.VisRgnChangedCallback = nil;
	for ( i = 0; i < kMaxMenuRects; i++ )
	{
		gBlitInfo.theMenuRects[i].left = 0;
		gBlitInfo.theMenuRects[i].top = 0;
		gBlitInfo.theMenuRects[i].right = 0;
		gBlitInfo.theMenuRects[i].bottom = 0;
	}
	
	// restore to original heap
	SetZone(theZone);
}


pascal void UnpatchBlitSupport( void )
{

	if (!gInterruptBlitPatchesInstalled) return;
	
	NSetTrapAddress(gOriginal_CopyBits, _CopyBits, (_CopyBits & 0x0800) ? ToolTrap : OSTrap);
	NSetTrapAddress(gOriginal_MenuSelect, _MenuSelect, (_MenuSelect & 0x0800) ? ToolTrap : OSTrap);
	NSetTrapAddress(gOriginal_PopupMenuSelect, _PopUpMenuSelect, (_PopUpMenuSelect & 0x0800) ? ToolTrap : OSTrap);
	NSetTrapAddress(gOriginal_MoveWindow, _MoveWindow, (_MoveWindow & 0x0800) ? ToolTrap : OSTrap);
	NSetTrapAddress(gOriginal_WaitNextEvent, _WaitNextEvent, (_WaitNextEvent & 0x0800) ? ToolTrap : OSTrap);
	NSetTrapAddress(gOriginal_CloseWindow, _CloseWindow, (_CloseWindow & 0x0800) ? ToolTrap : OSTrap);
	NSetTrapAddress(gOriginal_SelectWindow, _SelectWindow, (_SelectWindow & 0x0800) ? ToolTrap : OSTrap);
	NSetTrapAddress(gOriginal_PaintBehind, _PaintBehind, (_PaintBehind & 0x0800) ? ToolTrap : OSTrap);
	NSetTrapAddress(gOriginal_BringToFront, _BringToFront, (_BringToFront & 0x0800) ? ToolTrap : OSTrap);
	NSetTrapAddress(gOriginal_SendBehind, _SendBehind, (_SendBehind & 0x0800) ? ToolTrap : OSTrap);

	gInterruptBlitPatchesInstalled = FALSE;
}



pascal void my_CopyBitsPatch(long param1, long param2, long param3, long param4, short param5, long param6)
{
	int i;
	
	if ( gBlitInfo.theMenuIsDown )
	{
		Rect r = *(Rect*)param3; // source rect
		PixMap* pmp = (PixMap*)param1; // source pixmap
		
// I THINK that if it's blitting FROM anywhere with a non-zero y coordinate,
// that it's sucking it from the screen. How safe is this assumption?

// I further think that if it's blitting TO a location (0,0) with the same
// width and height as the "from" rectangle, that it's definitely saving
// the screen beneath where it's gonna put the menu.

		Rect srcR = *(Rect*)param3;
		Rect dstR = *(Rect*)param4;
		
		if ( srcR.top > 0 && dstR.left == 0 && dstR.top == 0 && dstR.right == (srcR.right - srcR.left) && dstR.bottom == (srcR.bottom - srcR.top) )
		{
			
			// Basically, if it's blitting FROM a non-zero y coordinate then
			// it's pulling it from the screen. So if that's the case then add
			// to this list of rectangles.
			
			Boolean bDealtWith = false;
			
			// OK, first, ensure that this rectangle isn't totally enclosed by
			// an already-existing rectangle. If so we don't need to remember THIS
			// one.
			
			for ( i = 0; i < kMaxMenuRects; i++ )
			{
				if ( gBlitInfo.theMenuRects[i].left <= srcR.left &&
					gBlitInfo.theMenuRects[i].top <= srcR.top &&
					gBlitInfo.theMenuRects[i].right >= srcR.right &&
					gBlitInfo.theMenuRects[i].bottom >= srcR.bottom )
				{
					bDealtWith = true;
				}
			}
			
			i = 0;
			while ( !bDealtWith )
			{
				if ( gBlitInfo.theMenuRects[i].left == 0 &&
					gBlitInfo.theMenuRects[i].top == 0 &&
					gBlitInfo.theMenuRects[i].right == 0 &&
					gBlitInfo.theMenuRects[i].bottom == 0 )
				{
					bDealtWith = true;
					gBlitInfo.theMenuRects[i] = srcR;
				}
				
				i++;
				if ( i >= kMaxMenuRects ) bDealtWith = true;
			}
		}

		r = *(Rect*)param4; // destination rect
		pmp = (PixMap*)param2;
	}
	CALL_SIX_PARAMETER_UPP( gOriginal_CopyBits, upp_CopyBitsProcInfo, param1, param2, param3, param4, param5, param6);

	// Now check to see if it's blitting BACK TO the screen -- ie, a menu is going
	// away. If it equals one of the menu rectangles that's currently saved and
	// remove it from the list if so.
	
	if ( gBlitInfo.theMenuIsDown )
	{
		Rect dstR = *(Rect*)param4;
		for ( i = 0; i < kMaxMenuRects; i++ )
		{
			if ( gBlitInfo.theMenuRects[i].left != 0 ||
				gBlitInfo.theMenuRects[i].top != 0 ||
				gBlitInfo.theMenuRects[i].right != 0 ||
				gBlitInfo.theMenuRects[i].bottom != 0 )
			{
				if ( dstR.left == gBlitInfo.theMenuRects[i].left &&
					dstR.top == gBlitInfo.theMenuRects[i].top &&
					dstR.right == gBlitInfo.theMenuRects[i].right &&
					dstR.bottom == gBlitInfo.theMenuRects[i].bottom )
				{
					// Hmmm, so it directly matches...
					gBlitInfo.theMenuRects[i].left = 0;
					gBlitInfo.theMenuRects[i].top = 0;
					gBlitInfo.theMenuRects[i].right = 0;
					gBlitInfo.theMenuRects[i].bottom = 0;
					if ( gBlitInfo.MenuUpCallback != nil )
					{
					    // call back that callback, passing in that surface object
					    ((MenuCallbackProc)gBlitInfo.MenuUpCallback)( &dstR );
					}
				}
			}
		}
	}
}

pascal long my_MenuSelectPatch(long param1)
{
	long result;
	int i;

	gBlitInfo.theMenuIsDown = true;
	for ( i = 0; i < kMaxMenuRects; i++ )
	{
		gBlitInfo.theMenuRects[i].left = 0;
		gBlitInfo.theMenuRects[i].top = 0;
		gBlitInfo.theMenuRects[i].right = 0;
		gBlitInfo.theMenuRects[i].bottom = 0;
	}
	
	result = CALL_ONE_PARAMETER_UPP( gOriginal_MenuSelect, upp_MenuSelectProcInfo, param1);
	
	for ( i = 0; i < kMaxMenuRects; i++ )
	{
		gBlitInfo.theMenuRects[i].left = 0;
		gBlitInfo.theMenuRects[i].top = 0;
		gBlitInfo.theMenuRects[i].right = 0;
		gBlitInfo.theMenuRects[i].bottom = 0;
	}
	gBlitInfo.theMenuIsDown = false;
	return result;
}

pascal long my_PopupMenuSelectPatch(long param1, short param2, short param3, short param4)
{
	long result;
	int i;

	gBlitInfo.theMenuIsDown = true;
	for ( i = 0; i < kMaxMenuRects; i++ )
	{
		gBlitInfo.theMenuRects[i].left = 0;
		gBlitInfo.theMenuRects[i].top = 0;
		gBlitInfo.theMenuRects[i].right = 0;
		gBlitInfo.theMenuRects[i].bottom = 0;
	}
	
	result = CALL_FOUR_PARAMETER_UPP( gOriginal_PopupMenuSelect, upp_PopupMenuSelectProcInfo, param1, param2, param3, param4);
	
	for ( i = 0; i < kMaxMenuRects; i++ )
	{
		gBlitInfo.theMenuRects[i].left = 0;
		gBlitInfo.theMenuRects[i].top = 0;
		gBlitInfo.theMenuRects[i].right = 0;
		gBlitInfo.theMenuRects[i].bottom = 0;
	}
	gBlitInfo.theMenuIsDown = false;

	return result;
}

pascal void my_MoveWindowPatch(long param1, short param2, short param3, Boolean param4)
{
	Boolean sameProcess = false;
	ProcessSerialNumber currentPSN;
	
	GetCurrentProcess( &currentPSN );
	SameProcess( &currentPSN, &gBlitInfo.clientPSN, &sameProcess );
	if ( sameProcess && gBlitInfo.VisRgnChangedCallback )
	{
		((VisRgnChangedCallbackProc)gBlitInfo.VisRgnChangedCallback)();
	}
	CALL_FOUR_PARAMETER_UPP( gOriginal_MoveWindow, upp_MoveWindowProcInfo, param1, param2, param3, param4);
}

pascal Boolean my_WaitNextEventPatch(short param1, long param2, long param3, long param4)
{
	Boolean result;
	
	BOOL bEventFilterAdjustedToSeeOSEvents = FALSE;
	
	if (!(param1 & osMask))
	{
		bEventFilterAdjustedToSeeOSEvents = TRUE;
		param1 |= osMask;
	}
	
	result = CALL_FOUR_PARAMETER_UPP( gOriginal_WaitNextEvent, upp_WaitNextEventProcInfo, param1, param2, param3, param4);
	
	BOOL bProcessedOSEvt = FALSE;
	{
	    EventRecord* event = (EventRecord*)param2;
	    if ( event->what == osEvt )
	    {
		bProcessedOSEvt = TRUE;
		if ( event->message & 0x01000000 ) // suspend/resume
		{
		    if ( event->message & 0x00000001 ) // resume
		    {
			gBlitInfo.thisApplicationIsFrontmost = true;
		    }
		    else
		    {
			gBlitInfo.thisApplicationIsFrontmost = false;
		    }
		}
		if (bEventFilterAdjustedToSeeOSEvents)
		{
		    // XXXbobclark: since they deliberately filtered out OS events, we won't
		    // give them any.
		    result = false;
		    event->what = nullEvent;
		    // XXXbobclark: I think that's the only field in the event record that I need to worry about.
		}
	    }
	    if ( event->what == updateEvt || event->what == activateEvt )
	    {
		gRecentUpdateEvent = true;
	    }
	}
	
	return result;
}

pascal void my_CloseWindowPatch(long param1)
{
	if ( gBlitInfo.VisRgnChangedCallback )
	{
	    ((VisRgnChangedCallbackProc)gBlitInfo.VisRgnChangedCallback)();
	}
	CALL_ONE_PARAMETER_UPP( gOriginal_CloseWindow, upp_CloseWindowProcInfo, param1);
}

pascal void my_SelectWindowPatch(long param1)
{
	if ( gBlitInfo.VisRgnChangedCallback )
	{
	    ((VisRgnChangedCallbackProc)gBlitInfo.VisRgnChangedCallback)();
	}
	CALL_ONE_PARAMETER_UPP( gOriginal_SelectWindow, upp_SelectWindowProcInfo, param1);
}

pascal void my_BringToFrontPatch(long param1)
{
	if ( gBlitInfo.VisRgnChangedCallback )
	{
	    ((VisRgnChangedCallbackProc)gBlitInfo.VisRgnChangedCallback)();
	}
	CALL_ONE_PARAMETER_UPP( gOriginal_BringToFront, upp_BringToFrontProcInfo, param1);
}

pascal void my_SendBehindPatch(long param1, long param2)
{
	if ( gBlitInfo.VisRgnChangedCallback )
	{
	    ((VisRgnChangedCallbackProc)gBlitInfo.VisRgnChangedCallback)();
	}
	CALL_TWO_PARAMETER_UPP( gOriginal_SendBehind, upp_SendBehindProcInfo, param1, param2);
}

pascal void my_PaintBehindPatch(long param1, long param2)
{
	if ( gBlitInfo.VisRgnChangedCallback )
	{
	    ((VisRgnChangedCallbackProc)gBlitInfo.VisRgnChangedCallback)();
	}
	CALL_TWO_PARAMETER_UPP( gOriginal_PaintBehind, upp_PaintBehindProcInfo, param1, param2);
}


#endif

