/*
**  CenterScreen.c  Version 37.3  (22. Oktober 1993)
**
**  Ein Commodity-Programm zum horizontalen zentrieren
**  eines Bildschirms. Benötigt Amiga-OS 2.04 oder höher.
**
**  (c) Copyright 1993 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 CenterScreen.c -o CenterScreen -3.0 -mi
*/


#include <clib/alib_protos.h>
#include <dos/dos.h>
#include <exec/memory.h>
#include <graphics/displayinfo.h>
#include <intuition/intuitionbase.h>
#include <intuition/screens.h>
#include <libraries/commodities.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>

#define   CxBase_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>

#define   CENTERSCREEN  1L

VOID   _main(VOID);
VOID   _waitwbmsg(VOID);
VOID   HandleEvent(VOID);
VOID   CenterScreen(struct Screen *);
VOID   MainLoop(VOID);
BOOL   InstallWedge(VOID);
VOID   RemoveWedge(VOID);
struct JumpTable *GetJumpTable(VOID);

__geta4 LONG (*oldOpenScreen) (__A0 struct NewScreen *, __A6 struct Library *);
__geta4 LONG newOpenScreen(__A0 struct NewScreen *, __A6 struct Library *);
__geta4 LONG (*oldOpenScreenTagList) (__A0 struct NewScreen *, __A1 struct TagItem *, __A6 struct Library *);
__geta4 LONG newOpenScreenTagList(__A0 struct NewScreen *, __A1 struct TagItem *, __A6 struct Library *);

IMPORT LVOOpenScreen;
IMPORT LVOOpenScreenTagList;
IMPORT struct IntuitionBase *IntuitionBase;
IMPORT struct WBStartup	    *_WBMsg;

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 NewBroker NewBroker =
{
    NB_VERSION,
    "CenterScreen",
    "CenterScreen 37.3 (22.10.93)",
    "Center horizontally a screen",
    NBU_NOTIFY | NBU_UNIQUE,
    0,
    0,
    NULL,
    0
};

struct LVOTable LVOArray[] =
{
    { &LVOOpenScreen,        (struct Library *) &IntuitionBase, &oldOpenScreen,	       &newOpenScreen	     },
    { &LVOOpenScreenTagList, (struct Library *) &IntuitionBase, &oldOpenScreenTagList, &newOpenScreenTagList }
};


STATIC const UBYTE VersionsTag[] = "\0$VER: CenterScreen 37.3 (22.10.93)";
STATIC const UBYTE DefKey[]	 = "control alt c";
STATIC const UBYTE YesStr[]	 = "yes";

STATIC const UBYTE JTName[]	 = "CenterScreen-JT";
#define		   JTNameLen	    15

BOOL   Auto,
       Wedged;
CxObj *Broker;
LONG  *ArgA[3];
       Priority;
UBYTE *KeyStr;
ULONG  CxSigFlag;


VOID
_main(VOID)
{
    if (IntuitionBase->LibNode.lib_Version < 37)
	_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, "CX_AUTO"))
		Auto = MatchToolValue(KeyStr, YesStr);

	    if (!(KeyStr = FindToolType(DObj->do_ToolTypes, "CX_HOTKEY")))
		KeyStr = DefKey;

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

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

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

	if (ArgA[1])
	    KeyStr = ArgA[1];
	else
	    KeyStr = DefKey;

	if (ArgA[2])

	    if (!Stricmp((STRPTR) ArgA[2], YesStr))
		Auto = TRUE;

	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);

		   switch (MsgType)
		   {
			   case CXM_IEVENT  : if (MsgID == CENTERSCREEN)
						  CenterScreen(IntuitionBase->FirstScreen);

					      break;

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

						      case CXCMD_ENABLE  : if (!Wedged && Auto)
									       Wedged = InstallWedge();

									   ActivateCxObj(Broker, TRUE);
									   break;

						      case CXCMD_DISABLE : if (Wedged && Auto)
									   {
									       RemoveWedge();
									       Wedged = FALSE;
									   }

									   ActivateCxObj(Broker, FALSE);
					      }
		   }
	    }
	}
    }
}


VOID
CenterScreen(struct Screen *Screen)
{
    struct DimensionInfo  DInfo;

    if (GetDisplayInfoData(NULL, (UBYTE *) &DInfo, sizeof(struct DimensionInfo), DTAG_DIMS, GetVPModeID(&Screen->ViewPort)))
    {
	UWORD MaxWidth = DInfo.TxtOScan.MaxX - DInfo.TxtOScan.MinX + 1,
	      DefWidth = Screen->Width;

	if (MaxWidth > DefWidth)
	    MoveScreen(Screen, ((MaxWidth - DefWidth) >> 1) - Screen->LeftEdge, NULL);
    }
}


VOID
MainLoop(VOID)
{
    CxObj *Key;
    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))
	    {
		if (Key = HotKey(KeyStr, BrokerMP, CENTERSCREEN))
		{
		    AttachCxObj(Broker, Key);
		    CxSigFlag = 1L << BrokerMP->mp_SigBit;

		    if (!CxObjError(Broker))
		    {
			ActivateCxObj(Broker, TRUE);

			if (Auto)
			    Wedged = InstallWedge();

			HandleEvent();

			if (Wedged)
			    RemoveWedge();

			ECode = RETURN_OK;
		    }
		}

		DeleteCxObjAll(Broker);
	    }
	    else
		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;
 
    Forbid();

    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;
	    }

    Permit();
    return(JumpTable);
}


__geta4 LONG
newOpenScreen(__A0 struct NewScreen *NewScreen, __A6 struct Library *Base)
{
    struct SignalSemaphore *JT;
    struct Screen	   *Screen = NULL;

    if (JT = FindSemaphore(JTName))
    {
	ObtainSemaphoreShared(JT);

	if (Screen = (struct Screen *) oldOpenScreen(NewScreen, (struct Library *) IntuitionBase))
	    CenterScreen(Screen);

	ReleaseSemaphore(JT);
    }

    return((LONG) Screen);
}


__geta4 LONG
newOpenScreenTagList(__A0 struct NewScreen *NewScreen, __A1 struct TagItem *TagItem, __A6 struct Library *Base)
{
    struct SignalSemaphore *JT;
    struct Screen	   *Screen = NULL;

    if (JT = FindSemaphore(JTName))
    {
	ObtainSemaphoreShared(JT);

	if (Screen = (struct Screen *) oldOpenScreenTagList(NewScreen, TagItem, (struct Library *) IntuitionBase))
	    CenterScreen(Screen);

	ReleaseSemaphore(JT);
    }

    return((LONG) Screen);
}
