/*
**  SPointer.c  Version 39.3  (14. April 1994)
**
**  Ein Commodity-Programm, welches die SetPointer()-Funktion patcht,
**  um den Mauszeiger in einer gewählten Auflösung darzustellen.
**  Benötigt Amiga-OS 3.0 oder höher.
**
**  (c) Copyright 1994 Ralf Seyfarth
**	Alle Rechte vorbehalten
**  
**  Dieses Programm ist FREEWARE, es darf frei weitergegeben werden,
**  solange alle Dateien im original Zustand enthalten sind.
**
**  Aufruf für DICE (2.07.56R) :
**
**  DCC SPointer.c -o SPointer -3.0 -mi
*/


#include <clib/alib_protos.h>
#include <dos/dos.h>
#include <exec/memory.h>
#include <graphics/gfx.h>
#include <graphics/gfxbase.h>
#include <intuition/intuitionbase.h>
#include <intuition/pointerclass.h>
#include <libraries/commodities.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>

#define   CxBase_DECLARED
#define   GfxBase_DECLARED
#define   IntuitionBase_DECLARED

#include <proto/commodities.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/icon.h>
#include <proto/intuition.h>
#include <proto/utility.h>

#include <stdlib.h>

VOID  _main(VOID);
VOID  _waitwbmsg(VOID);
VOID   HandleEvent(VOID);
VOID   MainLoop(VOID);
BOOL   InstallWedge(VOID);
VOID   RemoveWedge(VOID);
struct JumpTable    *GetJumpTable(VOID);
struct PointerTable *GetPTable(struct Window *, struct BitMap *, UBYTE *, UBYTE *, APTR);

__regargs VOID   AddPTable(struct PointerTable *);
__regargs VOID   RemovePTable(struct PointerTable *);
__regargs VOID   FreePTable(struct PointerTable *, ULONG);
__regargs struct PointerTable *FindPTable(struct Window *);

__geta4 VOID (*oldSetPointer) (__A0 struct Window *, __A1 UWORD *, __D0 LONG, __D1 LONG, __D2 LONG, __D3 LONG, __A6 struct Library *);
__geta4 VOID newSetPointer(__A0 struct Window *, __A1 UWORD *, __D0 LONG, __D1 LONG, __D2 LONG, __D3 LONG, __A6 struct Library *);
__geta4 VOID (*oldCloseWindow) (__A0 struct Window *, __A6 struct Library *);
__geta4 VOID newCloseWindow(__A0 struct Window *, __A6 struct Library *);

IMPORT LVOSetPointer;
IMPORT LVOCloseWindow;

IMPORT struct GfxBase	    *GfxBase;
IMPORT struct IntuitionBase *IntuitionBase;
IMPORT struct WBStartup	    *_WBMsg;

#define WordWidth   1L
#define PT_REMOVE   1L
#define PT_FREEVEC  2L

struct Library *CxBase;
struct MsgPort *BrokerMP;

struct LVOTable
{
    LONG 	    lt_LVO;
    struct Library *lt_LibBase;
    ULONG 	    lt_oldFunction;
    ULONG 	    lt_newFunction;
};

struct JumpTable
{
    struct SignalSemaphore  jt_Semaphore;
    UWORD		    jt_Word;
    struct Task		   *jt_Owner;
    UBYTE 		    jt_Function[12];
};

struct PointerTable
{
    struct PointerTable *pt_NextTable;
    struct PointerTable *pt_PrevTable;
    struct Window	*pt_Window;
    struct BitMap	*pt_BitMap;
    UBYTE	 	*pt_OldPlane0;
    UBYTE	 	*pt_OldPlane1;
    APTR	 	 pt_Object;
};

struct NewBroker NewBroker =
{
    NB_VERSION,
    "SPointer",
    "SPointer 39.3 (14.4.94)",
    "Copyright © 1994 Ralf Seyfarth",
    NBU_NOTIFY | NBU_UNIQUE,
    0,
    0,
    NULL,
    0
};

struct LVOTable LVOArray[] =
{
    { &LVOSetPointer,  (struct Library *) &IntuitionBase, &oldSetPointer,  &newSetPointer  },
    { &LVOCloseWindow, (struct Library *) &IntuitionBase, &oldCloseWindow, &newCloseWindow }
};

struct PointerTable *FirstPTable;
struct PointerTable *LastPTable;

STATIC const UBYTE VersionsTag[] = "\0$VER: SPointer 39.3 (14.4.94)";

STATIC const UBYTE JTName[]	 = "« SPointer »";
#define		   JTNameLen	    12

STATIC const UBYTE DefStr[]	 = "default";
STATIC const UBYTE NS1Str[]	 = "140ns";
STATIC const UBYTE NS7Str[]	 = "70ns";
STATIC const UBYTE NS3Str[]	 = "35ns";
STATIC const UBYTE ScrStr[]	 = "screenres";
STATIC const UBYTE LoStr[]	 = "lores";
STATIC const UBYTE HiStr[]	 = "highaspect";
STATIC const UBYTE ScrAStr[]	 = "screenresaspect";

BOOL   PTOn = TRUE,
       Wedged;
CxObj *Broker;
LONG  *ArgA[3],
       Priority;
UBYTE *KeyStr;
ULONG  CxSigFlag,
       XRes = POINTERXRESN_HIRES,
       YRes = POINTERYRESN_HIGH;


VOID
_main(VOID)
{
    if (IntuitionBase->LibNode.lib_Version < 39)
	_exit(RETURN_FAIL);

    if (_WBMsg)
    {
	struct DiskObject *DObj;

	CurrentDir(_WBMsg->sm_ArgList[0].wa_Lock);

	if (DObj = GetDiskObject(_WBMsg->sm_ArgList[0].wa_Name))
	{
	    if (KeyStr = FindToolType(DObj->do_ToolTypes, "CX_PRIORITY"))
		StrToLong(KeyStr, &Priority);

	    if (KeyStr = FindToolType(DObj->do_ToolTypes, "XRES"))

		if (MatchToolValue(KeyStr, DefStr))
		    XRes = POINTERXRESN_DEFAULT;

		else if (MatchToolValue(KeyStr, NS1Str))
		    XRes = POINTERXRESN_140NS;

		else if (MatchToolValue(KeyStr, NS7Str))
		    XRes = POINTERXRESN_70NS;

		else if (MatchToolValue(KeyStr, NS3Str))
		    XRes = POINTERXRESN_35NS;

		else if (MatchToolValue(KeyStr, ScrStr))
		    XRes = POINTERXRESN_SCREENRES;

		else if (MatchToolValue(KeyStr, LoStr))
		    XRes = POINTERXRESN_LORES;

	    if (KeyStr = FindToolType(DObj->do_ToolTypes, "YRES"))

		if (MatchToolValue(KeyStr, DefStr))
		    YRes = POINTERYRESN_DEFAULT;

		else if (MatchToolValue(KeyStr, HiStr))
		    YRes = POINTERYRESN_HIGHASPECT;

		else if (MatchToolValue(KeyStr, ScrStr))
		    YRes = POINTERYRESN_SCREENRES;

		else if (MatchToolValue(KeyStr, ScrAStr))
		    YRes = POINTERYRESN_SCREENRESASPECT;

	    FreeDiskObject(DObj);
	}
    }
    else
    {
	struct RDArgs *RDArgs;

	if (!(RDArgs = ReadArgs("CX_PRIORITY/N/K,XRES/K,YRES/K", ArgA, NULL)))
	{
	    PrintFault(IoErr(), NULL);
	    _exit(RETURN_FAIL);
	}

	if (ArgA[0])
	    Priority = *ArgA[0];

	if (ArgA[1])

	    if (!Stricmp((STRPTR) ArgA[1], DefStr))
		XRes = POINTERXRESN_DEFAULT;

	    else if (!Stricmp((STRPTR) ArgA[1], NS1Str))
		XRes = POINTERXRESN_140NS;

	    else if (!Stricmp((STRPTR) ArgA[1], NS7Str))
		XRes = POINTERXRESN_70NS;

	    else if (!Stricmp((STRPTR) ArgA[1], NS3Str))
		XRes = POINTERXRESN_35NS;

	    else if (!Stricmp((STRPTR) ArgA[1], ScrStr))
		XRes = POINTERXRESN_SCREENRES;

	    else if (!Stricmp((STRPTR) ArgA[1], LoStr))
		XRes = POINTERXRESN_LORES;

	if (ArgA[2])

	    if (!Stricmp((STRPTR) ArgA[2], DefStr))
		YRes = POINTERYRESN_DEFAULT;

	    else if (!Stricmp((STRPTR) ArgA[2], HiStr))
		YRes = POINTERYRESN_HIGHASPECT;

	    else if (!Stricmp((STRPTR) ArgA[2], ScrStr))
		YRes = POINTERYRESN_SCREENRES;

	    else if (!Stricmp((STRPTR) ArgA[2], ScrAStr))
		YRes = POINTERYRESN_SCREENRESASPECT;

	FreeArgs(RDArgs);
    }

    MainLoop();
}


VOID
HandleEvent(VOID)
{
    FOREVER
    {
	ULONG SigRec = Wait(SIGBREAKF_CTRL_C | CxSigFlag);

	if (SigRec & SIGBREAKF_CTRL_C)
	    return;

	if (SigRec & CxSigFlag)
	{
	    CxMsg *CxMsg;

	    while (CxMsg = GetMsg(BrokerMP))
	    {
		   ULONG MsgID,
			 MsgType;

		   MsgID   = CxMsgID(CxMsg);
		   MsgType = CxMsgType(CxMsg);

		   ReplyMsg((struct Message *) CxMsg);

		   if (MsgType == CXM_COMMAND)

		       switch (MsgID)
		       {
			       case CXCMD_KILL    :
			       case CXCMD_UNIQUE  : return;

			       case CXCMD_ENABLE  : PTOn = TRUE;
						    ActivateCxObj(Broker, TRUE);
						    break;

			       case CXCMD_DISABLE : PTOn = FALSE;
						    ActivateCxObj(Broker, FALSE);
		       }
	    }
	}
    }
}


VOID
MainLoop(VOID)
{
    CxMsg *CxMsg;
    ULONG  ECode = RETURN_FAIL;

    if (CxBase = OpenLibrary("commodities.library", 37L))
    {
	if (BrokerMP = CreateMsgPort())
	{
	    NewBroker.nb_Pri  = (BYTE) Priority;
            NewBroker.nb_Port = BrokerMP;

	    if (Broker = CxBroker(&NewBroker, NULL))
	    {
		CxSigFlag = 1L << BrokerMP->mp_SigBit;

		ActivateCxObj(Broker, TRUE);

		Wedged = InstallWedge();

		do
		  HandleEvent();
		while (FirstPTable);

		if (Wedged)
		    RemoveWedge();

		DeleteCxObjAll(Broker);
	    }

	    ECode = RETURN_OK;

	    while (CxMsg = GetMsg(BrokerMP))
		   ReplyMsg((struct Message *) CxMsg);

	    DeleteMsgPort(BrokerMP);
	}

	CloseLibrary(CxBase);
    }

    _exit(ECode);
    _waitwbmsg();
}


BOOL
InstallWedge(VOID)
{
    struct JumpTable *JumpTable;

    ULONG *AdrPtr;
    UWORD  i,
	   j;

    Forbid();

    if (JumpTable = GetJumpTable())
	 
	if (AttemptSemaphore((struct SignalSemaphore *) JumpTable))
	{
	    if (JumpTable->jt_Owner == NULL)
	    {
		JumpTable->jt_Owner = FindTask(0);
		 
		Disable();

		for (i = 2, j = 0; i < 12; i += 6, j++)
		{
		    AdrPtr = (ULONG *) ((UBYTE *) JumpTable->jt_Function + i);

		    (*((ULONG *) LVOArray[j].lt_oldFunction)) = (ULONG) *AdrPtr;

		    *AdrPtr = (ULONG) LVOArray[j].lt_newFunction;
		}

		CacheClearU();
		Enable();
	    }

	    ReleaseSemaphore((struct SignalSemaphore *) JumpTable);
	}

    Permit();
    return((BOOL) JumpTable);
}


VOID
RemoveWedge(VOID)
{
    struct JumpTable *JumpTable;

    ULONG *AdrPtr;
    UWORD  i,
	   j;

    Forbid();

    if (JumpTable = GetJumpTable())

	if (JumpTable->jt_Owner == FindTask(0))
	{
	    ObtainSemaphore((struct SignalSemaphore *) JumpTable);
	    Disable();

	    for (i = 2, j = 0; i < 12; i += 6, j++)
	    {
		 AdrPtr = (ULONG *) ((UBYTE *) JumpTable->jt_Function + i);
		*AdrPtr = (*((ULONG *) LVOArray[j].lt_oldFunction));
	    }

	    CacheClearU();
	    Enable();

	    JumpTable->jt_Owner = NULL;

	    ReleaseSemaphore((struct SignalSemaphore *) JumpTable);
	}

    Permit();
}


struct JumpTable *
GetJumpTable(VOID)
{
    struct JumpTable *JumpTable;

    UBYTE *JTStr;
    ULONG *AdrPtr;
    UWORD *JmpIns,
	   i,
	   j;
 
    if (!(JumpTable = (struct JumpTable *) FindSemaphore(JTName)))

	if (JumpTable = AllocVec(sizeof(struct JumpTable), MEMF_PUBLIC | MEMF_CLEAR))

	    if (JTStr = AllocVec(JTNameLen + 1, MEMF_PUBLIC | MEMF_CLEAR))
	    {
		for (i = 0, j = 0; i < 12; i += 6, j++)
		{
		     JmpIns = (UWORD *) ((UBYTE *) JumpTable->jt_Function + i);
		    *JmpIns = 0x4EF9;

		     AdrPtr = (ULONG *) ((UBYTE *) JumpTable->jt_Function + i + 2);
		    *AdrPtr = (ULONG) SetFunction((struct Library *) (*((ULONG *) LVOArray[j].lt_LibBase)), LVOArray[j].lt_LVO, (VOID *) ((UBYTE *) JumpTable->jt_Function + i));
		}

		CopyMem(JTName, JTStr, JTNameLen + 1);

		JumpTable->jt_Semaphore.ss_Link.ln_Pri  = 0;
		JumpTable->jt_Semaphore.ss_Link.ln_Name = JTStr;

		AddSemaphore((struct SignalSemaphore *) JumpTable);
	    }
	    else
	    {
		FreeVec(JumpTable);
		JumpTable = NULL;
	    }

    return(JumpTable);
}


__regargs VOID
AddPTable(struct PointerTable *PointerTable)
{
    if (FirstPTable)
    {
	LastPTable->pt_NextTable   = PointerTable;
	PointerTable->pt_PrevTable = LastPTable;
    }
    else
    {
	FirstPTable		   = PointerTable;
	PointerTable->pt_PrevTable = NULL;
    }

    LastPTable 		       = PointerTable;
    PointerTable->pt_NextTable = NULL;
}


__regargs VOID
RemovePTable(struct PointerTable *PointerTable)
{
    if (FirstPTable)
    {
	if (PointerTable->pt_PrevTable)
	    PointerTable->pt_PrevTable->pt_NextTable = PointerTable->pt_NextTable;
	else
	    FirstPTable = PointerTable->pt_NextTable;

	if (PointerTable->pt_NextTable)
	    PointerTable->pt_NextTable->pt_PrevTable = PointerTable->pt_PrevTable;
	else
	    LastPTable = PointerTable->pt_PrevTable;
    }
}


__regargs struct PointerTable *
FindPTable(struct Window *Window)
{
    struct PointerTable *PointerTable = FirstPTable;

    if (FirstPTable)

	do
	{
	  if (PointerTable->pt_Window == Window)
	      return(PointerTable);

	  PointerTable = PointerTable->pt_NextTable;
	}
	while (PointerTable);

    return(NULL);
}


struct PointerTable *
GetPTable(struct Window *Window, struct BitMap *BitMap, UBYTE *OldPlane0, UBYTE *OldPlane1, APTR Object)
{
    struct PointerTable *PointerTable;

    Forbid();

    if (PointerTable = FindPTable(Window))
        FreePTable(PointerTable, NULL);

    else if (PointerTable = AllocVec(sizeof(struct PointerTable), MEMF_PUBLIC | MEMF_CLEAR))
	AddPTable(PointerTable);

    if (PointerTable)
    {
	PointerTable->pt_Window    = Window;
	PointerTable->pt_BitMap    = BitMap;
	PointerTable->pt_OldPlane0 = OldPlane0;
	PointerTable->pt_OldPlane1 = OldPlane1;
	PointerTable->pt_Object    = Object;

	Permit();
	return(PointerTable);
    }
    else
    {   Permit();
	return(NULL);
    }
}


__regargs VOID
FreePTable(struct PointerTable *PointerTable, ULONG Flags)
{
    if (PointerTable)
    {
	if ((Flags & PT_REMOVE) == PT_REMOVE)
	    RemovePTable(PointerTable);

	PointerTable->pt_BitMap->Planes[0] = PointerTable->pt_OldPlane0;
	PointerTable->pt_BitMap->Planes[1] = PointerTable->pt_OldPlane1;

	DisposeObject(PointerTable->pt_Object);
	FreeBitMap(PointerTable->pt_BitMap);

	if ((Flags & PT_FREEVEC) == PT_FREEVEC)
	    FreeVec(PointerTable);
    }
}


__geta4 VOID
newSetPointer(__A0 struct Window *Window, __A1 UWORD *Pointer, __D0 LONG Height, __D1 LONG Width, __D2 LONG XOffset, __D3 LONG YOffset, __A6 struct Library *Base)
{
    struct SignalSemaphore *JT;

    if (JT = FindSemaphore(JTName))
    {
	BOOL OldPointer = FALSE;

	struct BitMap *BitMap;

	ObtainSemaphoreShared(JT);

	if (PTOn && (XRes != POINTERXRESN_DEFAULT || YRes != POINTERYRESN_DEFAULT))
	{
	    if (BitMap = AllocBitMap(Width, Height, 2, BMF_INTERLEAVED | BMF_CLEAR, NULL))
	    {
		BOOL   RemPtr    = FALSE;
		UBYTE *OldPlane0 = BitMap->Planes[0],
		      *OldPlane1 = BitMap->Planes[1];
		ULONG  Object;

		BitMap->Planes[0] = Pointer + 2;
		BitMap->Planes[1] = Pointer + 3;

		if (Object = (ULONG) NewObject(NULL,
					       "pointerclass",
					       POINTERA_BitMap,      BitMap,
					       POINTERA_XOffset,     XOffset,
					       POINTERA_YOffset,     YOffset,
					       POINTERA_WordWidth,   WordWidth,
					       POINTERA_XResolution, XRes,
					       POINTERA_YResolution, YRes,
					       TAG_DONE))
		{
		    if (GetPTable(Window, BitMap, OldPlane0, OldPlane1, (APTR) Object))
			SetWindowPointer(Window, WA_Pointer, Object, TAG_DONE);
		    else
			RemPtr = TRUE;
		}
		else
		    RemPtr = TRUE;

                if (RemPtr)
		{
		    OldPointer	      = TRUE;
		    BitMap->Planes[0] = OldPlane0;
		    BitMap->Planes[1] = OldPlane1;

		    if (Object)
			DisposeObject((APTR) Object);

		    FreeBitMap(BitMap);
		}
	    }
	    else
	        OldPointer = TRUE;
	}
	else
	    OldPointer = TRUE;

	if (OldPointer)
	    oldSetPointer(Window, Pointer, Height, Width, XOffset, YOffset, (struct Library *) IntuitionBase);

	ReleaseSemaphore(JT);
    }
}


__geta4 VOID
newCloseWindow(__A0 struct Window *Window, __A6 struct Library *Base)
{
    struct SignalSemaphore *JT;

    if (JT = FindSemaphore(JTName))
    {
	ObtainSemaphoreShared(JT);
	Forbid();
	FreePTable(FindPTable(Window), PT_REMOVE | PT_FREEVEC);
	Permit();
	oldCloseWindow(Window, (struct Library *) IntuitionBase);
	ReleaseSemaphore(JT);
    }
}
