/*********************************************************************\
**                               ________________________________    **
**    A n t h o n y             |________    __    __    ________|   **
**                                       |  |o_|  |o_|  |            **
**            T h y s s e n            __|   __    __   |__          **
**                                  __|   __|  |  |  |__   |__       **
**   `` Dragon Computing ! ''    __|   __|     |  |     |__   |__    **
**                              |_____|        |__|        |_____|   **
**                                                                   **
\*********************************************************************/
/*  This file has routines to allow us to 'monitor' an Intuition port
**
**    What happens, is that we create a port, `OurPort', and set things
**  up so that GetMsg() works normally for OurPort, and the original
**  port, WBPort.  However, PutMsg() to OurPort will put the new message
**  on WBPort, and vice-versa.  Basically, the heads of both message
**  lists are normal, but the two tails have been swapped.  Since
**  GetMsg() uses the head of the list, it acts normally.  PutMsg() uses
**  the tail of the list, so it acts differently.  (the sigBit and
**  sigTasks have also been swapped)
**
**    What this means, is that anything PutMsg()'ed to the WBPort (an
**  intuiMessage for the Workbench), will signal us, and we can then
**  GetMsg() it.  When we are finished looking at that message, we can
**  give it to WBPort by a PutMsg() to OurPort.
*/

#include "AMenu.h"

extern struct MsgPort   *WBReplyPort;
extern int               WBMenus, WBProcesses;
extern BOOL   AddMenuStrip();
extern void   DelMenuStrip(), WBFree(), RunItem();

static struct MsgPort  *WBPort;
static struct List     *WBMsgList;
static LONG             WBSignal, OurSignal;
static BYTE             OrigPri;


/* our port */
static struct MsgPort OurPort = {
     {NULL, NULL, NT_MSGPORT, 0, NULL},
     PA_SIGNAL, 0, NULL,
     {NULL, NULL, NULL, NT_MESSAGE, 0}
  };

static struct List *OurMsgList = &(OurPort.mp_MsgList);

/*-----------------------------------------------------*/

void
AttachMsgPort()
/* fiddle with the two ports, setting things up */
{
  Forbid();

    /* get the info we need */
  WBPort    = M->WBWindow->UserPort;
  WBMsgList = &(WBPort->mp_MsgList);
  WBSignal  = WBPort->mp_SigBit;

    /* setup our port */
  OurPort.mp_SigBit    = WBSignal;
  OurPort.mp_SigTask   = WBPort->mp_SigTask;

    /* flip things around */
  WBPort->mp_SigTask = M->Handler;
  OurMsgList->lh_Head = (struct Node *)&(WBMsgList->lh_Tail);
  OurMsgList->lh_TailPred = WBMsgList->lh_TailPred;
  WBMsgList->lh_TailPred = (struct Node *)&(OurMsgList->lh_Head);
  OurMsgList->lh_TailPred->ln_Succ = (struct Node *)&(OurMsgList->lh_Tail);

  Permit();

    /* prevent deadlocks - adjust our Prioity above that of Workbench */
  OrigPri = SetTaskPri(M->Handler, OurPort.mp_SigTask->tc_Node.ln_Pri+1);
}


void
DeleteMsgPort()
/* Restore things the way they were before make_MsgPort()
** Note that we don't refer to winPort.  This is because we
** may have had someone else 'monitor' this port (such as MonIDCMP)
** besides us, since we attached to it.
*/
{
  register struct Message *TmpMsg;
  register struct MsgPort *TmpPort;

  Forbid();
    /* clean out our list */
  while ((TmpMsg = GetMsg(&OurPort)) != NULL) PutMsg(&OurPort, TmpMsg);

     /* find MsgPort that OurMsgList->lh_Head belongs to */
     /*      (the port we've been PutMsg'ing to)         */
  TmpPort = (struct MsgPort *) (
    (UBYTE*) &OurPort - (UBYTE*) &(OurPort.mp_MsgList.lh_Tail)
    + (UBYTE*)OurMsgList->lh_Head);

    /* restore things */
  OurMsgList->lh_Head->ln_Pred = OurMsgList->lh_TailPred;
  OurMsgList->lh_TailPred->ln_Succ = OurMsgList->lh_Head;
  TmpPort->mp_SigTask = OurPort.mp_SigTask;
  Permit();

  SetTaskPri(M->Handler, OrigPri);    /* return prioity to normal */
}


void
FinishMonitor()
  /* clean up everything */
{
  DeleteMsgPort();
  FreeSignal(OurSignal);
}


BOOL
StartMonitor()
  /* do all appropriate initialization of monitor */
{
  AttachMsgPort();
    /* we need to allocate the same signal, so that it doesn't get
       allocated to something else, messing us up */
  OurSignal = AllocSignal(WBSignal);
  if (OurSignal != WBSignal) {
    FinishMonitor();
    return FALSE;
  }
  return TRUE;
}


/* Check if Menu code is from our menu items */
struct ExtMenuItem *
MatchItem(Code)
  USHORT Code;
{
  if( MENUNUM( Code ) < WBMenus )
    return NULL;                 /* a workbench menu */
  return (struct ExtMenuItem *) ItemAddress(M->WBWindow->MenuStrip, Code);
}


/* The actual monitor.  Just keep waiting for messages, and acting on them
 * Note that if we don't use a certain IDCMP message, we PutMsg it to get
 * it back to the window it was meant for.
 */
#define CtrlC     SIGBREAKF_CTRL_C   /* Exit   Signal */
#define CtrlD     SIGBREAKF_CTRL_D   /* Update Signal */
#define CtrlMask  (CtrlC|CtrlD)

void
Monitor()
{
  register struct IntuiMessage  *Msg;
  register struct ExtMenuItem   *EItem;
  register ULONG                 RetMask, WaitMask, WBReplyMask;

  WBReplyMask = 1L << WBReplyPort->mp_SigBit;

  WaitMask = (1L << WBSignal) | WBReplyMask | CtrlMask;

  while( TRUE ) {
    RetMask = Wait(WaitMask);

    if( RetMask & CtrlC )       /* ^C - RemoveHandler in progress */
      break;

    if( RetMask & CtrlD ) {     /* ^D - Update menus */
      DB( FPrintf(DBWindow, "AMenu Update\n"); )
      DelMenuStrip();
      DB( FPrintf(DBWindow, "  Signaling Parent ready to update\n"); )
      Signal(M->Parent, (1 << M->ParentSig));
      if( Wait(CtrlMask) & CtrlC )
        break;     /* if ^C - RemoveHandler in progress - else continue */
      DB( FPrintf(DBWindow, "  Parent Signals Update complete\n"); )
      AddMenuStrip();     /* ignore return - it always does it */
      continue;
    }

    if( RetMask & WBReplyMask ) {     /* a WB program Replied */
      register struct Message *WBMsg;
      DB( FPrintf(DBWindow, "WB Program Exited -- Freeing Message\n"); )
      while ((WBMsg = GetMsg(WBReplyPort)) != NULL)
        WBFree(WBMsg);
      continue;
    }
                                         /* Intercepted a WB IDCMP message */
    while( Msg = (struct IntuiMessage *)GetMsg(&OurPort) ) {
      switch( Msg->Class ) {
      case MENUPICK:
        if( EItem = MatchItem(Msg->Code) ) {
            /* Found one of our menu selections */
          ReplyMsg((struct Message *)Msg);   /* reply directly */
          DB( FPrintf(DBWindow, "Menu selection `%s'\n",
                ((struct IntuiText *)EItem->MenuItem.ItemFill)->IText); )
          RunItem(EItem);                    /* run the indicated program */
        } else
          PutMsg(&OurPort, (struct Message *)Msg);   /* pass on to WB */
        break;
      case NEWPREFS:
        DB( FPrintf(DBWindow, "NewPrefs recieved - reattaching\n"); )
        PutMsg(&OurPort, (struct Message *)Msg);   /* pass on to WB */
         /* loop until menus detached by WB and then we reattach them */
        while( !AddMenuStrip() )
          Delay(30);
        DB( FPrintf(DBWindow, "-- DONE\n"); )
        break;
      default:
        PutMsg(&OurPort, (struct Message *)Msg);   /* pass on to WB */
      }
    }
  }
}

