/* PowerWindows: PowerWindows.c */

#include <exec/types.h>
#include <exec/ports.h>
#include <exec/interrupts.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <graphics/gfx.h>
#include <graphics/gfxmacros.h>
#include <graphics/gfxbase.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <intuition/imageclass.h>
#include <intuition/gadgetclass.h>
#include <intuition/classusr.h>
#include <utility/utility.h>
#include <utility/tagitem.h>
#include <libraries/gadtools.h>
#include <Workbench/Workbench.h>
#include <Workbench/startup.h>

#include <devices/input.h>
#include <graphics/layers.h>
#include <graphics/clip.h>
#include <utility/utility.h>
#include <utility/tagitem.h>

#include <pragma/exec_lib.h>
#include <pragma/dos_lib.h>
#include <pragma/intuition_lib.h>
#include <pragma/graphics_lib.h>
#include <pragma/layers_lib.h>
#include <pragma/utility_lib.h>
#include <pragma/gadtools_lib.h>
#include <pragma/timer_lib.h>
#include <pragma/icon_lib.h>
#include <pragma/commodities_lib.h>
#include <clib/alib_protos.h>

#pragma header

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

#include "config.h"
#include "PowerWindows.h"
#include "PWDevelopper.h"


char *VerString = "$VER: PowerWindows "_ProgVersionS_" ("__DATE2__")";
char *AuthString = "$AUTH: (C) Copyright 1999 by Georg Steger";

static struct InputEvent CloseWindowIE = {0,IECLASS_CLOSEWINDOW};
static BOOL RomPatched;

struct Library *InputBase;

/* Wichtig: braucht DOS 36.147, wegen
             - ParsePatternNoCase und
             - MatchPatternNoCase !!  */

/* PatchTable */

/* Reihenfolge nicht aendern!!!!, weil ScanPreferences eventuell
   die Window-Funktionen auf TRUE setzen muss!!!! */
   
#define NumPatches (sizeof(PatchTable)/sizeof(struct PatchEntry))

struct PatchEntry PatchTable[] =
/*  0 */	{{TRUE,LIB_INTUITION,LVO_OpenWindow,New_OpenWindow,Old_OpenWindow},
/*  1 */	 {TRUE,LIB_INTUITION,LVO_OpenWindowTagList,New_OpenWindowTagList,Old_OpenWindowTagList},
/*  2 */	 {TRUE,LIB_INTUITION,LVO_CloseWindow,New_CloseWindow,Old_CloseWindow},
/*  3 */	 {TRUE,LIB_INTUITION,LVO_FreeSysRequest,New_FreeSysRequest,Old_FreeSysRequest},
/*  4 */	 {TRUE,LIB_INTUITION,LVO_ActivateWindow,New_ActivateWindow,Old_ActivateWindow},
/*  5 */	 {TRUE,LIB_INTUITION,LVO_ActivateGadget,New_ActivateGadget,Old_ActivateGadget},
/*  6 */	 {TRUE,LIB_INTUITION,LVO_WindowToFront,New_WindowToFront,Old_WindowToFront},
/*  7 */	 {TRUE,LIB_INTUITION,LVO_WindowToBack,New_WindowToBack,Old_WindowToBack},
/*  8 */	 {TRUE,LIB_INTUITION,LVO_OpenScreen,New_OpenScreen,Old_OpenScreen},
/*  9 */	 {TRUE,LIB_INTUITION,LVO_OpenScreenTagList,New_OpenScreenTagList,Old_OpenScreenTagList},
/* 10 */	 {TRUE,LIB_INTUITION,LVO_CloseScreen,New_CloseScreen,Old_CloseScreen},
/*	11 */	 {TRUE,LIB_INTUITION,LVO_OpenWorkBench,New_OpenWorkBench,Old_OpenWorkBench},
/* 12 */	 {TRUE,LIB_INTUITION,LVO_CloseWorkBench,New_CloseWorkBench,Old_CloseWorkBench},
/* 13 */	 {TRUE,LIB_INTUITION,LVO_EasyRequestArgs,New_EasyRequestArgs,Old_EasyRequestArgs},
/* 14 */	 {TRUE,LIB_INTUITION,LVO_AutoRequest,New_AutoRequest,Old_AutoRequest},
/* 15 */	 {TRUE,LIB_LAYERS,LVO_UpfrontLayer,New_UpfrontLayer,Old_UpfrontLayer},
/* 16 */	 {TRUE,LIB_LAYERS,LVO_MoveLayerInFrontOf,New_MoveLayerInFrontOf,Old_MoveLayerInFrontOf},
/* 17 */	 {TRUE,LIB_LAYERS,LVO_CreateUpfrontHookLayer,New_CreateUpfrontHookLayer,Old_CreateUpfrontHookLayer},
/* 18 */	 {TRUE,LIB_LAYERS,LVO_CreateUpfrontLayer,New_CreateUpfrontLayer,Old_CreateUpfrontLayer},
/* 19 */  {FALSE,LIB_INTUITION,LVO_SetWindowTitles,New_SetWindowTitles,Old_SetWindowTitles},
/* 20 */  {FALSE,LIB_INTUITION,LVO_NewObjectA,New_NewObjectA,Old_NewObjectA}
};



/* Ende ======================================================================*/

void Ende(char *mes)
{
	struct Message *msg;

	WORD rc;
	
	rc=RETURN_OK;
	if (mes)
	{
		rc=RETURN_ERROR;
		if (!IntuitionBase)
		{
			printf("PowerWindows: %s\n",mes);
		} else {
			IntNachricht(mes,"Arrgh!");
		}
	}

#ifdef DOROMPATCH
#ifdef ROMHACK
	if (RomPatched)
	{
		ResetROM();
	}
#endif
#endif

	if (DeadLockTask) RemTask(DeadLockTask);
	if (MyConfig.TaskSpeicher) FreeVec(MyConfig.TaskSpeicher);
	if (MyConfig.IconifyTaskSpeicher) FreeVec(MyConfig.IconifyTaskSpeicher);
	
	if (IO_HandlerDa)
	{
		IO_Req->io_Command=IND_REMHANDLER;
		IO_Req->io_Data= &IO_Int;
		DoIO((struct IORequest *)IO_Req);
	}
	
	if (IO_Req)
	{
		if (IO_Req->io_Device) CloseDevice((struct IORequest *)IO_Req);
		DeleteIORequest((struct IORequest *)IO_Req);
	}
	if (IO_MP) DeleteMsgPort(IO_MP);
			
	if (ActionPort)
	{
		while((msg=GetMsg(ActionPort)))
		{
			FreeVec(msg);
		}
		DeleteMsgPort(ActionPort);
	}
	
	if (FreezeSignal!=-1) FreeSignal(FreezeSignal);
	if (OpaqueSignal!=-1) FreeSignal(OpaqueSignal);
	if (DepthGadSignal!=-1) FreeSignal(DepthGadSignal);

	if (MyImageClass) FreeClass (MyImageClass);
	if (MyButtonClass) FreeClass (MyButtonClass);
	
	KillExt (MyConfig.ScreenChecker_Seg,MyConfig.ScreenChecker);
	KillExt (MyConfig.WindowChecker_Seg,MyConfig.WindowChecker);
	KillExt (MyConfig.IconRenderer_Seg,MyConfig.IconRenderer);
	
	if (PrefsScanner_Seg) UnLoadSeg(PrefsScanner_Seg);

	if (MustResetCD)
	{
		UnLock(CurrentDir(OldCD));
	}

	if (MyConfig.IconFont) CloseFont(MyConfig.IconFont);

	EndCx();

	if (ProgSemHere) RemSemaphore(&ProgrammSemaphore.Sem);

	KillLibrary(LayersBase);
	KillLibrary(UtilityBase);
	KillLibrary(GadToolsBase);
	KillLibrary(IconBase);
	KillLibrary(CxBase);

	exit (rc);
}

/* Init =====================================================================*/

void Init(void)
{	
	static char *ProgSemName = "PowerWindows Warp Core";
	struct PWSemaphore *pwsem;
	BOOL OK=FALSE;

	OS_Version=((struct ExecBase *)SysBase)->LibNode.lib_Version;
	WORKBENCHNAME="Workbench";
	
	FreezeSignal=-1;OpaqueSignal=-1;DepthGadSignal=-1;

	MyConfig.PatchTable=PatchTable;
	MyConfig.TaskList=&TaskList;
	MyConfig.IconifyTaskList=&IconifyTaskList;
	
	IO_HandlerDa=FALSE;
	MyConfig.IO_MaxMoveCounter=20;
	IO_LayerMagic=0;
	IO_RectDrawn=FALSE;
	
	IO_Int.is_Node.ln_Type=NT_UNKNOWN;
	IO_Int.is_Node.ln_Name="PowerWindows: Input-Handler";
	IO_Int.is_Node.ln_Pri=51;
	IO_Int.is_Data=(APTR)0;
	IO_Int.is_Code=(void *)InputEntry;
	
	InputTask=FindTask("input.device");
	MainTask=FindTask(0);
	
	NewList (&ScreenList);
	NewList (&TaskList);
	NewList (&IconifyTaskList);
	NewList (&IconList);
	
	InitSemaphore(&ScreenListSemaphore);
	InitSemaphore(&DragSemaphore);
	InitSemaphore(&IconListSemaphore);
	InitSemaphore(&ProgrammSemaphore.Sem);

/* Dummy-Hooks zur schnellen Erkennung von Layers */

	DummyHook.h_Entry=(HOOKFUNC)Dummy_HookFunc;
	SuperLayerHook.h_Entry=(HOOKFUNC)Dummy_HookFunc;
	IconLayerHook.h_Entry=(HOOKFUNC)Dummy_HookFunc;
	MagicMenuLayerHook.h_Entry=(HOOKFUNC)Dummy_HookFunc;

/**/

	Forbid();
	if (!(pwsem=(struct PWSemaphore *)FindSemaphore(ProgSemName)))
	{
		OK=TRUE;

		ProgrammSemaphore.Sem.ss_Link.ln_Name=ProgSemName;
		ProgrammSemaphore.Sem.ss_Link.ln_Pri=-128;
		ProgrammSemaphore.PWTask=MainTask;
		ProgrammSemaphore.Match1=0x12345678;
		ProgrammSemaphore.Match2=0x87654321;
		ProgrammSemaphore.Version=_ProgVersion_;
		ProgrammSemaphore.Revision=_ProgRevision_;
		ProgrammSemaphore.SubRevision=_ProgSubRevision_;

		AddSemaphore(&ProgrammSemaphore.Sem);
		ProgSemHere=TRUE;
	}
	Permit();

	if (!OK)
	{
		if ((pwsem->Match1==0x12345678) && (pwsem->Match2==0x87654321))
		{
			Signal(pwsem->PWTask,SIGBREAKF_CTRL_C);
			Ende(0);
		}
	}

	if (!(ActionPort=CreateMsgPort())) Ende("Can't create Action-MsgPort!");
	ActionMask=(1L << (ActionPort->mp_SigBit));

	FreezeSignal=AllocSignal(-1);
	OpaqueSignal=AllocSignal(-1);
	DepthGadSignal=AllocSignal(-1);

	if ((FreezeSignal==-1) || (OpaqueSignal==-1) || (DepthGadSignal==-1)) Ende("Can't alloc Signals!");

	FreezeMask=(1L << FreezeSignal);
	OpaqueMask=(1L << OpaqueSignal);
	DepthGadMask=(1L << DepthGadSignal);

	MyConfig.ScanIt=FALSE;
	MyConfig.AllPlanes=FALSE;
	MyConfig.AnyTasksThere=FALSE;
	MyConfig.AnyIconifyTasksThere=FALSE;
	MyConfig.MarkerTyp=MARKER_NORMAL;
	
	MyConfig.ButtonDOWN=IECODE_LBUTTON;
	MyConfig.ButtonUP=MyConfig.ButtonDOWN | IECODE_UP_PREFIX;
	MyConfig.NormalQuali=0;
	MyConfig.DoNormalQuali=FALSE;

	MyConfig.ForceButtonDOWN=IECODE_LBUTTON;
	MyConfig.ForceButtonUP=MyConfig.ForceButtonDOWN | IECODE_UP_PREFIX;
	MyConfig.ForceQuali=IEQUALIFIER_LSHIFT|IEQUALIFIER_LALT;
	MyConfig.DoForceQuali=TRUE;

	MyConfig.MultiButtonDOWN=IECODE_LBUTTON;
	MyConfig.MultiButtonUP=MyConfig.MultiButtonDOWN | IECODE_UP_PREFIX;
	MyConfig.MultiQuali=IEQUALIFIER_LALT|IEQUALIFIER_CONTROL;
	MyConfig.DoMultiQuali=TRUE;

	MyConfig.LockDelay_Sec=1;
	MyConfig.LockDelay_Mic=0;
	MyConfig.IconifyOffset=0;
	MyConfig.MouseFaktor=4;
	MyConfig.TaskXOR=0;
	MyConfig.IconifyTaskXOR=0;
	MyConfig.CaseSensitive=FALSE;
	MyConfig.IconifyCaseSensitive=FALSE;
	MyConfig.NoMouseFreeze=FALSE;
	MyConfig.SafeMoveWindow=FALSE;
	Icons=TRUE;
	MyConfig.IconImageType=ICONIMAGE_NORMAL;
	MyConfig.IconWidthFac=1;
	MyConfig.IconWidthDiv=6;
	MyConfig.DoIconWidthDiv=TRUE;
	MyConfig.MinIconWidth=100;
	MyConfig.IconPosition=IP_UPPERLEFT;
	MyConfig.IconSpaceX=1;
	MyConfig.IconSpaceY=1;
	MyConfig.MinWinWidth=50;
	MyConfig.IconDepth=ID_ALWAYSFRONT;
	MyConfig.ScreenChecker=Dummy_ScreenChecker;
	MyConfig.WindowChecker=Dummy_WindowChecker;

	MyConfig.OpaqueMAXDEPTH=32;
	MyConfig.OpaqueMAXPIXELS=0x7FFFFFFF;
	MyConfig.OpaqueMAXBYTES= 0x7FFFFFFF;
}

/* ParseArguments ==========================================================*/

#ifndef DOROMPATCH

	#define ARG_TEMPLATE "HOMEDIR=H/K,CONFIGFILE=C/K"

#else

	extern "ASM"
	{
		APTR ROMStart;
		APTR ROMBufferStart;
	};

	#define ARG_TEMPLATE "HOMEDIR=H/K,CONFIGFILE=C/K,ROMADDRESS/A,ROMBUFFERADDRESS/A"

#endif

enum {ARG_HOMEDIR,ARG_CONFIGFILE,ARG_ROM,ARG_ROMBUFFER,NUM_ARGS};

void ParseArguments(void)
{
	static char *ERRSTRING_LOCK = "Can't lock directory \"%s\" (HOMEDIR %s)!";
	struct RDArgs *MyArgs;
	struct DiskObject *ProgramIcon;
	STRPTR name;
	char	*Args[NUM_ARGS];
	WORD	i;
	BPTR	lk;
	
	if (!FromWB)
	{
		for(i=0;i<NUM_ARGS;i++)
		{
			Args[i]=0;
		}
		MyArgs=ReadArgs(ARG_TEMPLATE,(LONG *)Args,0);
		if (!MyArgs)
		{
			Fault(IoErr(),0,s,255);
			Ende(s);
		}
		if (Args[ARG_HOMEDIR])
		{
			if ((lk=Lock(Args[ARG_HOMEDIR],ACCESS_READ)))
			{
				OldCD=CurrentDir(lk);
				MustResetCD=TRUE;
			} else {
				sprintf(s,ERRSTRING_LOCK,Args[ARG_HOMEDIR],"argument");
				FreeArgs(MyArgs);
				Ende(s);
			}
		}
		if (Args[ARG_CONFIGFILE])
		{
			strncpy(ExtConfigFile,Args[ARG_CONFIGFILE],255);
		}
		
#ifdef DOROMPATCH
		
		ROMStart=(APTR)strtol((char *)Args[ARG_ROM],0,0);
		ROMBufferStart=(APTR)strtol((char *)Args[ARG_ROMBUFFER],0,0);

#endif

		FreeArgs(MyArgs);
	} else {
		lk=Lock("PROGDIR:",ACCESS_READ);
		if (lk)
		{
			OldCD=CurrentDir(lk);
			MustResetCD=TRUE;
		}

		if (WBMessage->sm_NumArgs==2) i=1; else i=0;
	
		lk=CurrentDir(WBMessage->sm_ArgList[i].wa_Lock);
		ProgramIcon=GetDiskObject(WBMessage->sm_ArgList[i].wa_Name);
		lk=CurrentDir(lk);
		if (ProgramIcon)
		{
			name=FindToolType((STRPTR *)ProgramIcon->do_ToolTypes,"HOMEDIR");
			if (name)
			{
				if ((lk=Lock(name,ACCESS_READ)))
				{
					lk=CurrentDir(lk);
					if (MustResetCD)
					{
						UnLock(lk);
					} else {
						MustResetCD=TRUE;
						OldCD=lk;
					}
				} else {
					sprintf(s,ERRSTRING_LOCK,name,"tooltype");
					FreeDiskObject(ProgramIcon);
					Ende(s);
				}				
			}
			if ((name=FindToolType((STRPTR *)ProgramIcon->do_ToolTypes,"CONFIGFILE")))
			{
				strncpy(ExtConfigFile,name,255);
			}
			
#ifdef DOROMPATCH
			if ((name=FindToolType((STRPTR *)ProgramIcon->do_ToolTypes,"ROMADDRESS")))
			{
				ROMStart=(APTR)strtol(name,0,0);
			}
			if ((name=FindToolType((STRPTR *)ProgramIcon->do_ToolTypes,"ROMBUFFERADDRESS")))
			{
				ROMBufferStart=(APTR)strtol(name,0,0);
			}
#endif
			FreeDiskObject(ProgramIcon);
		}
	}
	
#ifdef DOROMPATCH

	if (!ROMStart || !ROMBufferStart) Ende("You forgot to set ROMADDRESS and/or ROMBUFFERADDRESS!");
	if (memcmp(ROMStart,ROMBufferStart,100)) Ende("ROMADDRESS and/or ROMBUFFERADDRESS is wrong!");

#endif
}

/* OpenLibs =====================================================================*/

void OpenLibs(void)
{
	if (!(LayersBase=OpenLibrary("layers.library",36))) Ende("Layers.library V36?");
	if (!(UtilityBase=OpenLibrary("utility.library",36))) Ende("Utility.library V36?");
	if (!(GadToolsBase=OpenLibrary("gadtools.library",36))) Ende("Gadtools.library V36?");
	if (!(IconBase=OpenLibrary("icon.library",36))) Ende("Icon.library V36?");
	if (!(CxBase=OpenLibrary("commodities.library",36))) Ende ("Commodities.library V36?");
	if (!IntuitionBase || !GfxBase) Ende ("Library missing - fatal!");	
}

/* OpenInput =====================================================================*/

void OpenInput(void)
{
	ULONG quali;

	if (!(IO_MP=CreateMsgPort())) Ende("Input-MsgPort?");
	if (!(IO_Req=(struct IOStdReq *)CreateIORequest(IO_MP,sizeof(struct IOStdReq)))) Ende("Input-IO-Request?");
	if (OpenDevice("input.device",0,(struct IORequest *)IO_Req,0)) Ende("Input.device?");
	InputBase=(struct Library *)IO_Req->io_Device;
	quali=PeekQualifier();

	if ((quali&(IEQUALIFIER_LALT|IEQUALIFIER_LSHIFT|IEQUALIFIER_LEFTBUTTON))==(IEQUALIFIER_LALT|IEQUALIFIER_LSHIFT|IEQUALIFIER_LEFTBUTTON)) Ende(0);
	if ((quali&(IEQUALIFIER_RSHIFT|IEQUALIFIER_LEFTBUTTON))==(IEQUALIFIER_RSHIFT|IEQUALIFIER_LEFTBUTTON)) ExtDebug=TRUE;
	
}

/* IntNachricht ==================================================================*/

int IntNachricht(char *body,char *auswahl)
{
	struct EasyStruct es = {sizeof(es),
		                     0,
		                     "PowerWindows",
		                     body,
		                     auswahl};
	

	return IntuitionBase ? EasyRequestArgs(0,&es,0,0) : -1;
}

/* Hinweis ======================================================================*/

void Hinweis(char *body)
{
	if (IntuitionBase)
	{
		IntNachricht(body,"OK!");
	} else {
		printf("PowerWindows: %s",body);
	}
}

/* MyLockLayer ==================================================================*/

BOOL MyLockLayer(struct Layer *lay)
{
	if (!MyConfig.SafeLockLayer)
	{
		LockLayer(0,lay);
		return TRUE;
	} else {
		return AttemptSemaphore(&lay->Lock);
	}
}

/* MyLockLayers ================================================================*/

BOOL MyLockLayers(struct Layer_Info *li)
{
	BOOL ret=FALSE;

	if (!MyConfig.SafeLockLayer)
	{
		LockLayers(li);
		ret=TRUE;
	} else {

		Forbid();

		if (IO_ScreenEntry->se_LayerInfo->LockLayersCount==0)
		{
			if (AttemptSemaphore(&li->Lock))
			{
				ReleaseSemaphore(&li->Lock);
				LockLayers(li);
				ret=TRUE;
			}
		}
		
		Permit();

	}
	return ret;
}

/* PrintList =====================================================================*/

#ifdef DebugMe

void PrintList(void)
{
	struct ScreenEntry *se;

	ObtainSemaphoreShared(&ScreenListSemaphore);
	if (IsListEmpty(&ScreenList))
	{
		printf("Liste ist leer\n");
	} else {
		se=(struct ScreenEntry *)ScreenList.lh_Head;
		while (se->se_Node.mln_Succ)
		{
			printf("Screen: %lx\nL-Info: %lx\nLayer1: %lx\nLayer2: %lx\n",se->se_Screen,se->se_LayerInfo,se->se_RightLayer,se->se_BottomLayer);
			
			printf("(%d,%d)-(%d,%d)\n",se->se_RightLayer->bounds.MinX,
												 se->se_RightLayer->bounds.MinY,
												 se->se_RightLayer->bounds.MaxX,
												 se->se_RightLayer->bounds.MaxY);
			printf("(%d,%d)-(%d,%d)\n\n",se->se_BottomLayer->bounds.MinX,
												 se->se_BottomLayer->bounds.MinY,
												 se->se_BottomLayer->bounds.MaxX,
												 se->se_BottomLayer->bounds.MaxY);
			se=(struct ScreenEntry *)se->se_Node.mln_Succ;
		}
	}
	ReleaseSemaphore(&ScreenListSemaphore);
}

#endif


/* ScanScreens ====================================================================*/

void ScanScreens(void)
{
	struct Screen *scr;

	if (MyConfig.ScanIt)
	{
		Forbid();
		scr=IntuitionBase->FirstScreen;
		ObtainSemaphore(&ScreenListSemaphore);
		while (scr)
		{
			After_OpenScreen(scr);
			ScanWindows(scr);			
			scr=scr->NextScreen;
		} /* while (scr) */
		ReleaseSemaphore(&ScreenListSemaphore);
		Permit();

	} // if (MyConfig.ScanIt)
}

/* GetLibrary ====================================================================*/

struct Library *GetLibrary(WORD num)
{
	switch (num)
	{
		case LIB_LAYERS: return LayersBase;
		case LIB_INTUITION: return (struct Library *)IntuitionBase;
	}
}

/* DoPatches ======================================================================*/

void DoPatches(void)
{
	WORD i;
	
	if (MyConfig.NoReq || FindPort("CyReq"))
	{
		PatchTable[13].DoPatch=FALSE;	/* EasyRequestArgs() nicht patchen */
		PatchTable[14].DoPatch=FALSE; /* AutoRequest() nicht patchen */
	}

	if (MyConfig.RefreshIconTitles) PatchTable[19].DoPatch=TRUE;
	if (MyConfig.NoDGPatch) PatchTable[20].DoPatch=FALSE;				// NewObjectA() nicht patchen */

	Disable();
	for (i=0;i<NumPatches;i++)
	{
		if (PatchTable[i].DoPatch)
		{
			PatchTable[i].OldFunction=SetFunction(GetLibrary(PatchTable[i].LibID),PatchTable[i].LVO,PatchTable[i].NewFunction);
		}
	}
	Enable();
}

/* RemovePatches =================================================================== */

BOOL RemovePatches(void)
{
	WORD i,i2;
	APTR oldfunc[NumPatches];
	
	
	Disable();
	for (i=0;i<NumPatches;i++)
	{
		if (PatchTable[i].DoPatch)
		{
			oldfunc[i]=SetFunction(GetLibrary(PatchTable[i].LibID),PatchTable[i].LVO,PatchTable[i].OldFunction);

			if (oldfunc[i]!=PatchTable[i].NewFunction)
			{
				for (i2=0;i2<=i;i2++)
				{
					if (PatchTable[i2].DoPatch) SetFunction(GetLibrary(PatchTable[i2].LibID),PatchTable[i2].LVO,oldfunc[i2]);
				}
				Enable();
				return FALSE;
			}
		}
	}
	Enable();

	IO_Req->io_Command=IND_REMHANDLER;
	IO_Req->io_Data= &IO_Int;
	DoIO((struct IORequest *)IO_Req);
	IO_HandlerDa=FALSE;
	return TRUE;
}

/* AddInputHandler =================================================================== */

void AddInputHandler(void)
{
	IO_Req->io_Command=IND_ADDHANDLER;
	IO_Req->io_Data= &IO_Int;
	
	DoIO((struct IORequest *)IO_Req);
	IO_HandlerDa=TRUE;
}

/* RemoveWindows =================================================================== */

void RemoveWindows(struct ScreenEntry *se)
{
	struct Window *nwin;
	struct Gadget *gad,*ngad;
	WORD wx,wy;
	BOOL ok,dorefresh;

	do {
		ok=TRUE;
		win=se->se_Screen->FirstWindow;
		while (win)
		{
			nwin=win->NextWindow;
			if (win->WLayer) if (win->WLayer->BackFill == &IconLayerHook)
			{
				KillIconWindow(win,TRUE);
				nwin=se->se_Screen->FirstWindow;
			} else {

				gad=win->FirstGadget;
				dorefresh=FALSE;
				while (gad)
				{
					ngad=gad->NextGadget;
					if (KillMyGadget(gad,win)) dorefresh=TRUE;
					gad=ngad;
				}			
	
				if (dorefresh) RefreshWindowFrame(win);


				if (((win->LeftEdge+win->Width)>se->se_Screen->Width) ||
				   ((win->TopEdge+win->Height)>se->se_Screen->Height) ||
				   (win->LeftEdge<0) ||
				   (win->TopEdge<0))
				{
					wx=(win->LeftEdge+win->Width)>se->se_NormalWidth ? se->se_Screen->Width - win->Width : win->LeftEdge;
					wy=(win->TopEdge+win->Height)>se->se_NormalHeight ? se->se_Screen->Height - win->Height : win->TopEdge;
					if (wx<0) wx=0;
					if (wy<0) wy=0;					
					
					ChangeWindowBox(win,wx,wy,
											  LowerOne(win->Width,se->se_NormalWidth),
											  LowerOne(win->Height,se->se_NormalHeight));
					ok=FALSE;
				}
			} /* not IconWindow */
			win=nwin;
		}	/* while (win) */
		Delay(1);
	} while (!ok);
}

/* SelfDestruction=================================================================== */

void SelfDestruction(void)
{
	struct ScreenEntry *se;
	struct ScreenEntry *se_next;

	se=(struct ScreenEntry *)ScreenList.lh_Head;
	
	ObtainSemaphore(&ScreenListSemaphore);
	while (se->se_Node.mln_Succ)
	{
		RemoveWindows(se);
		Delay(2);

		KillLayer(se->se_RightLayer);
		KillLayer(se->se_BottomLayer);
		KillLayer(se->se_TopLayer);
		KillLayer(se->se_LeftLayer);

#ifdef ROMHACK
		Forbid();
		se->se_LayerInfo->Flags &= (~LIF_PATCHED);
		Permit();
#endif

		se_next=(struct ScreenEntry *)se->se_Node.mln_Succ;
		Remove((struct Node *)se);
		FreeVec(se);
		se=se_next;
	}
	ReleaseSemaphore(&ScreenListSemaphore);

}

/* MakePrivateClasses ==================================================================*/

void MakePrivateClasses(void)
{
	extern ULONG HookEntry();
	
	MyImageClass=MakeClass(0,ImageSuperClass,0,sizeof(MyImageData),0);
	if (!MyImageClass)
	{
		MyConfig.DoIconify=FALSE;
		return;
	}
	MyImageClass->cl_Dispatcher.h_Entry=HookEntry;
	MyImageClass->cl_Dispatcher.h_SubEntry=(HOOKFUNC)MyImageDispatcher;

	MyButtonClass=MakeClass(0,ButtonSuperClass,0,sizeof(MyButtonData),0);
	if (!MyButtonClass)
	{
		FreeClass(MyImageClass);
		MyConfig.DoIconify=FALSE;
		return;
	}
	MyButtonClass->cl_Dispatcher.h_Entry=HookEntry;
	MyButtonClass->cl_Dispatcher.h_SubEntry=(HOOKFUNC)MyButtonDispatcher;
}


/* MakeDeadLockTask ============================================================*/

void MakeDeadLockTask(void)
{
	DeadLockError=0;
	
	DeadLockTask=CreateTask("PowerWindows: Deadlock Hunter",MyConfig.HunterPri,DeadLockHunter,4096);
	if (!DeadLockTask) Ende("Deadlock-Hunter-SubTask?");

	Wait(SIG_TASK_READY);
	
	switch (DeadLockError)
	{
		case DT_ERR_NOTIMERPORT:
			Ende ("Timer-MsgPort?");
		case DT_ERR_NOTIMERREQUEST:
			Ende ("Timer-IO-Request?");
		case DT_ERR_NOTIMERDEVICE:
			Ende ("Timer.device?");
	}
}

/* StartDeadLockTast ==========================================================*/

void StartDeadLockTask(void)
{
	Signal(DeadLockTask,SIG_TASK_START);
}
	
/* Deadlock Hunter  =============================================================*/

void DeadLockHunter(void)
{
	ULONG signals;
	
	/* Open Timer-Device */
	
	DeadLockError=0;

	if (!(Timer_MP=CreateMsgPort()))
	{
		DeadLockError=DT_ERR_NOTIMERPORT;
	} else {
	
		if (!(Timer_Req=(struct timerequest *)CreateIORequest(Timer_MP,sizeof(struct timerequest))))
		{
			DeadLockError=DT_ERR_NOTIMERREQUEST;
			DeleteMsgPort(Timer_MP);Timer_MP=0;
		} else {

			if (OpenDevice("timer.device",UNIT_VBLANK,(struct IORequest *)Timer_Req,0))
			{
				DeadLockError=DT_ERR_NOTIMERDEVICE;
				DeleteIORequest((struct IORequest *)Timer_Req);Timer_Req=0;
				DeleteMsgPort(Timer_MP);Timer_MP=0;
			}

		} // if (Timer_Req)
		
	} // if (Timer_MP)

	if (DeadLockError)
	{
		Signal(MainTask,SIG_TASK_READY);
		Wait(0);
	}

	TimerBase=(struct Library *)Timer_Req->tr_node.io_Device;

	TimerMask=(1L << (Timer_MP->mp_SigBit));

	Signal(MainTask,SIG_TASK_READY);
	
	Wait(SIG_TASK_START);				/* Wait for Start-Signal */
		
	Timer_Req->tr_node.io_Command=TR_ADDREQUEST;
	Timer_Req->tr_time.tv_secs=MyConfig.LockDelay_Sec;
	Timer_Req->tr_time.tv_micro=MyConfig.LockDelay_Mic;
	SendIO((struct IORequest *)Timer_Req);
	
	for (;;)								/* Endlos-Schleife */
	{
		signals=Wait(TimerMask|SIG_TASK_DIE);
		if (signals & TimerMask)
		{
			if (GetMsg(Timer_MP))				/* mindestens ein Message  muss da sein! Mehr
			                                  als einer duerfte nicht moeglich sein! */
			{
				while(GetMsg(Timer_MP));		/* Kill Messages */
				
				if (IO_EntryCounter==IO_EntryCounter_Old)
				{
					Forbid();
					IO_Action=ACTION_NONE;
					if (IO_RectDrawn)
					{
						DrawRect();
					}
					if (IO_LayerLocked)
					{
						UnlockLayers(IO_ScreenEntry->se_LayerInfo);
						IO_LayerLocked=FALSE;
					}
					if (IO_CloseWindowLocked)
					{
						ReleaseSemaphore(&DragSemaphore);
						IO_CloseWindowLocked=FALSE;
					}
					Permit();
				}
			
				if (!QuitMe)
				{
					Timer_Req->tr_time.tv_secs=MyConfig.LockDelay_Sec;
					Timer_Req->tr_time.tv_micro=MyConfig.LockDelay_Mic;
					SendIO((struct IORequest *)Timer_Req);	/* Fire off a new Request */
				}
				IO_EntryCounter_Old=IO_EntryCounter;
			}
		}
		if (signals & SIG_TASK_DIE)
		{
			AbortIO((struct IORequest *)Timer_Req);
			WaitIO((struct IORequest *)Timer_Req);
			while (GetMsg(Timer_MP));
			CloseDevice((struct IORequest *)Timer_Req);
			DeleteIORequest((struct IORequest *)Timer_Req);
			DeleteMsgPort(Timer_MP);
			Signal(MainTask,SIG_TASK_READY);					/* Damit der Task nicht zu fruh gekillt wird!! */
			Wait(0);
			Blinker();		/* hierher duerfte der Task nie kommen!! */
		}
	}	/* Endlos-Schleife */
}

	
/* HandleBreak =================================================================*/

void HandleBreak(void)
{
	if (!(QuitMe=RemovePatches()))
	{
		DisplayBeep(IntuitionBase->ActiveScreen);
	}
}
										  
/* HandleActionPort =============================================================*/

#define OK_NO 0
#define OK_OK 1
#define OK_FAST 2

void HandleActionPort(void)
{
	WORD wx,wy,OK_Code;
	ULONG qualifiers;
	ULONG ilock;

	while ((MyMsg=(struct ActionMsg *)GetMsg(ActionPort)))
	{
		switch (MyMsg->Code)
		{
			case AM_MOVEWINDOW:
			case AM_ICONIFY:
			case AM_ACTIVATEWINDOW:

				#ifdef DebugMe
				if (MyMsg->Code==AM_ICONIFY) printf("You want me to iconify %s\n",MyMsg->win->Title);
				if (MyMsg->Code==AM_MOVEWINDOW) printf("You want me to move %s\n",MyMsg->win->Title);
				if (MyMsg->Code==AM_ACTIVATEWINDOW) printf("You want me to activate %s\n",MyMsg->win->Title);
				#endif
		
				qualifiers=PeekQualifier();

				OK_Code=OK_NO;

				ilock=LockIBase(0);
				scr=IntuitionBase->FirstScreen;
				while (scr)
				{
					if (scr==MyMsg->win->WScreen)
					{
						win=scr->FirstWindow;
						while (win)
						{
							if (win==MyMsg->win)
							{
								wx=win->LeftEdge;
								wy=win->TopEdge;
#ifndef ROMHACK								
								if (MyMsg->Code==AM_MOVEWINDOW)
								{
									if (((MyMsg->x1+MyMsg->win->Width)<=MyMsg->SEntry->se_NormalWidth) &&
									    ((MyMsg->y1+MyMsg->win->Height)<=MyMsg->SEntry->se_NormalHeight))
									{
#endif
										OK_Code=OK_FAST; /* Fast-Mode Window ist innerhalb normaler Ausmasses des Screens! */
										break;

#ifndef ROMHACK
									}
								}
								OK_Code=OK_OK;
								break;
#endif
							}
							win=win->NextWindow;
						}
						break;
					}
					scr=scr->NextScreen;
				}
				UnlockIBase(ilock);

				if ((OK_Code!=OK_NO) && (MyMsg->IconWindow)) KillIconWindow(MyMsg->IconWindow,TRUE);

				if (OK_Code!=OK_OK)
				{
					if (OK_Code==OK_FAST)
					{

#ifndef ROMHACK
						if (MyConfig.Opaque)
						{
							MyMsg->win->WScreen->Width=MyMsg->SEntry->se_NormalWidth;
							MyMsg->win->WScreen->Height=MyMsg->SEntry->se_NormalHeight;
						}
#else
						
						if (MyMsg->Code == AM_ICONIFY)
						{
							MyMsg->x1 = MyMsg->win->WScreen->Width-MyConfig.IconifyOffset;	/* Ziel-Koordinate-X fuer Fenster */
							MyMsg->y1 = MyMsg->win->TopEdge;	/* Ziel-Koordinate-Y fuer Fenster */
						} else if (MyMsg->Code == AM_ACTIVATEWINDOW)
						{
							ActivateWindow(MyMsg->win);
							break;
						}
#endif


						if ((MyMsg->x1 != MyMsg->win->LeftEdge) || (MyMsg->y1 != MyMsg->win->TopEdge))
						{
							MoveWindow(MyMsg->win,MyMsg->x1-MyMsg->win->LeftEdge,MyMsg->y1-MyMsg->win->TopEdge);
						}
						ActivateWindow(MyMsg->win);
						if (Icons && (MyMsg->IconWindow))
						{
							 if (MyConfig.PopToFront ^ (0 != (qualifiers&(IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT)))) // ^ = nur eines von beiden darf TRUE sein
							 {
									WindowToFront(MyMsg->win);
							 }
						}
						if (Icons && (!MyMsg->IconWindow)) MakeIcon(MyMsg,wx,wy);
					}
					break; /* go out of "switch MyMsg->Code" */
				} /* */
					
				if (MyMsg->Code==AM_ACTIVATEWINDOW)
				{
					ActivateWindow(MyMsg->win);
					break; /* go out of "switch MyMsg->Code" */
				}
				
#ifndef ROMHACK
				Please_Go_Inactive=TRUE;	/* Handler schickt Freeze-Signal wenn er
				                            inaktiv wurde! */
				Wait(FreezeMask);
				ObtainSemaphoreShared(&ScreenListSemaphore);
				Forbid();
				MySE=FindScreen(MyMsg->win->WLayer->LayerInfo);
				if (MySE)
				{
					IO_ScreenEntry=MySE;
					IO_Win=MyMsg->win;
					IO_Scr=IO_Win->WScreen;
					IO_a1=MySE->se_NormalWidth;	/* normale Breite merken */
					IO_b1=MySE->se_NormalHeight;/* normale Hoehe merken */

					switch (MyMsg->Code)
					{
						case AM_MOVEWINDOW:
							IO_x1=MyMsg->x1;
							IO_y1=MyMsg->y1;
							break;
							
						case AM_ICONIFY:
							IO_x1=IO_a1-MyConfig.IconifyOffset;	/* Ziel-Koordinate-X fuer Fenster */
							IO_y1=IO_Win->TopEdge;	/* Ziel-Koordinate-Y fuer Fenster */
							break;
							
					}

					IO_Action=ACTION_AFTERMOVE;
					IO_MoveCounter=0;
					IO_Scr->Width=16384;			/* Screen Breite aendern */
					IO_Scr->Height=16384;			/* Screen Hoehe aendern */
				}
				ReleaseSemaphore(&ScreenListSemaphore);
				if (MySE)
				{
					if ((IO_x1 != IO_Win->LeftEdge) || (IO_y1 != IO_Win->TopEdge))
					{
						MoveWindow(IO_Win,IO_x1-IO_Win->LeftEdge,IO_y1-IO_Win->TopEdge);
					}
					ActivateWindow(IO_Win);
					if (Icons && (MyMsg->IconWindow) && MyConfig.PopToFront)
					{
						 if (MyConfig.PopToFront ^ (0 != (qualifiers&(IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT)))) // ^ = nur eines von beiden darf TRUE sein
						 {
								WindowToFront(MyMsg->win);
						 }
					}
				}
				
				Permit();
				if (MySE)
				{
					if (Icons && (!MyMsg->IconWindow)) MakeIcon(MyMsg,wx,wy);
				}
				break;
#endif

			case AM_ICONCLOSEGADGET:
				for (wx=0;wx<5;wx++)
				{
					ActivateWindow(MyMsg->win);
					Delay(1);
					if (IntuitionBase->ActiveWindow==MyMsg->win) break;
				}
				AddIEvents(&CloseWindowIE);
				break;
				
		}	/* switch (MyMsg->Code) */
		FreeVec(MyMsg);
	}	/* while (MyMsg) */
}

/* HandleOpaque =================================================================*/

void HandleOpaque(void)
{
	WORD x1,y1;

	x1=IO_OpaqueX-IO_Win->LeftEdge;
	y1=IO_OpaqueY-IO_Win->TopEdge;
	if (x1 || y1)
	{
		MoveWindow(IO_Win,x1,y1);
	}
}

/* HandleDepthGad =============================================================*/

void HandleDepthGad(void)
{
	struct Layer *wlay,*lay;
	ULONG ilock;
	BYTE Action=0;		// 1 = front 0 = back

	if (DEPTH_Win)
	{
		wlay=lay=DEPTH_Win->WLayer;

		LockLayers(wlay->LayerInfo);

		ilock=LockIBase(0);

		while ((lay=lay->front))
		{
			if (((struct Window *)lay->Window != DEPTH_Win) &&
			    (lay->BackFill != &SuperLayerHook) &&
			    ((lay->BackFill != &IconLayerHook) || (MyConfig.IconDepth!=ID_ALWAYSFRONT)))
			{
				if ((lay->bounds.MinY <= wlay->bounds.MaxY && lay->bounds.MaxY >= wlay->bounds.MinY) &&
				    (lay->bounds.MinX <= wlay->bounds.MaxX && lay->bounds.MaxX >= wlay->bounds.MinX)) 
				{
					Action=1;break;
				}
			}
		}

		UnlockIBase(ilock);
		
		UnlockLayers(wlay->LayerInfo);
		
		if (Action==1) WindowToFront(DEPTH_Win); else WindowToBack(DEPTH_Win);
	}
}

/* MAIN =================================================================== */

void main(void)
{
	Init();
	OpenLibs();
	ParseArguments();
	OpenInput();DBUG("1");
	ScanPreferences(NumPatches);DBUG("2");
	MakeDeadLockTask();DBUG("3");
	
	MakePrivateClasses();DBUG("4");

/*	ImageTester();
	printf("Waiting: ");
	scanf("%ld",&l);*/

	InitCx();DBUG("5");
	DoPatches();DBUG("6");
	ScanScreens();DBUG("7");
	AddInputHandler();DBUG("8");

#ifdef DOROMPATCH
#ifdef ROMHACK
	PatchROM();DBUG("RomPatch");
	RomPatched=TRUE;
#endif
#endif

	StartDeadLockTask();DBUG("9");
	
	QuitMe=FALSE;
	do {
		Signals=Wait(CxMask|ActionMask|OpaqueMask|DepthGadMask|SIGBREAKF_CTRL_C);

		if (Signals & ActionMask) HandleActionPort();
		if (Signals & CxMask) HandleCx();
		if (Signals & OpaqueMask) HandleOpaque();
		if (Signals & DepthGadMask) HandleDepthGad();

		if (QuitMe || (Signals & SIGBREAKF_CTRL_C)) HandleBreak();
/*		PrintList();*/
			
	} while (!QuitMe);

	Signal(DeadLockTask,SIG_TASK_DIE);
	Wait(SIG_TASK_READY);					/* Warten bis Task aufgerauemt hat!! */	

	SelfDestruction();

//	#ifdef DebugMe
	if (MyButtonClassCounter || MyImageClassCounter)
	{
		sprintf(s,"Button-Counter: %ld\n",MyButtonClassCounter);
		Hinweis(s);
		sprintf(s,"Image-Counter : %ld\n",MyImageClassCounter);
		Hinweis(s);
	}
//	#endif
	
	Ende(0);
}

/* WBMAIN  -- WB ENTRY -- ===================================================== */

void wbmain(struct WBStartup *WBMes)
{
	WBMessage=WBMes;
	FromWB=TRUE;
	main();
}
	

 
