MAG Disk (Jun 1990) : source / Main.c

#include "PopUpMenu.h"
#include "Version.h"

#define REPLYPORTNAME  "Reply"
#define IMPDEVPORTNAME "ImpDev"
#define TIMERPORTNAME  "Timer"
#define QUITPORTNAME   "Quit"
#define INPHANDLNAME   PopUpTaskName
#define SEMAPHORENAME  PopUpTaskName

#define POPUPMSG       "\x9B0;33mPopUpMenu\x9B0m "
#define KILLMSG        "removed.\n"
#define STARTMSG       "installed.\n\xA9 Martin Adrian 1990\n"
#define FAILMSG        "failed.\n"
#define DELAYTIME      150

#define INPHANDLPRI    53
/* Must use kickstart 1.2 or higher */
#define LIBVERSION     33

/* don't know how to find these in C */
#define LVOSetMenuStrip   -0x108
#define LVOClearMenuStrip -0x36
#define LVOOnMenu	  -0xc0
#define LVOOffMenu	  -0xb4

/* this is for errors in proto/exec.h */
#undef RemSemaphore
#pragma syscall RemSemaphore 25e 901

extern VOID InitPopUpMenu(VOID);
extern VOID MySetMenuStrip();
extern VOID MyClearMenuStrip();
extern VOID MyOnMenu();
extern VOID MyOffMenu();

VOID InitPopUpMenu()
{

  IMPORT struct DosLibrary     *DosBase;
  IMPORT struct IntuitionBase  *IntuitionBase;
  IMPORT struct GfxBase        *GfxBase;
  IMPORT struct LayersBase     *LayersBase;

  IMPORT struct IOStdReq       *InputReqBlock;
  IMPORT struct timerequest    *TimerReqBlock;
  IMPORT struct SignalSemaphore PopUpSemaphore;
  IMPORT __fptr OldSetMenuStrip, OldClearMenuStrip, OldOnMenu, OldOffMenu;
  IMPORT BPTR StdOut;
  IMPORT BPTR PopUpSeg;

  IMPORT STRPTR far PopUpTaskName; /* Pointer to the string "PopUpMenu" */

  struct MsgPort      *ReplyPort;
  struct MsgPort      *InputDevPort;
  struct MsgPort      *TimerPort;
  struct Interrupt    InputReqData;
  struct SignalData   InputSignals;
  LONG		      MenuUpSigNum, MenuDownSigNum;
  LONG		      MouseMovedSigNum, SelectDownSigNum;

  geta4();   /* load global database */

  Write(StdOut,POPUPMSG,sizeof(POPUPMSG));

  /***********************************
   * see if we are already installed *
   ***********************************/
  if (!(ReplyPort = FindPort(REPLYPORTNAME))) {
    if (!(ReplyPort = CreatePort(REPLYPORTNAME,0)))
      goto CleanUp13;
  }
  else {
    /* yes, popupmenues already installed, tell running task to quit */
    struct MsgPort *const QuitPort = CreatePort(QUITPORTNAME,0);
    struct IntuiMessage *Message;

    if (QuitPort) {
      if (Message = BuildIntuiMsg(QuitPort,QUITPOPUPMENU,NULL)) {
	/* Send quitmessage */
	PutMsg(ReplyPort, (struct Message *)Message);
	/* Wait for reply */
	WaitPort(QuitPort);
	/* get rid of the message */
	GetMsg(QuitPort);
	FreeMem(Message,(LONG)Message->ExecMessage.mn_Length +
				sizeof(struct Message));

	WriteAndClose(KILLMSG, sizeof(KILLMSG));
      }
      DeletePort(QuitPort);
    }
    goto CleanUp13;
  }

  /******************
   * open libraries *
   ******************/
  if (!(IntuitionBase = (struct IntuitionBase *)
			OpenLibrary("intuition.library", LIBVERSION)))
    goto CleanUp12;
  if (!(GfxBase       = (struct GfxBase *)
			OpenLibrary("graphics.library", LIBVERSION)))
    goto CleanUp11;
  if (!(LayersBase    = (struct LayersBase *)
			OpenLibrary("layers.library", LIBVERSION)))
    goto CleanUp10;

  /************************
   * Allocate our signals *
   ************************/
  if ((MenuUpSigNum     = AllocSignal(-1)) == -1)
    goto CleanUp9;
  if ((MenuDownSigNum   = AllocSignal(-1)) == -1)
    goto CleanUp8;
  if ((MouseMovedSigNum = AllocSignal(-1)) == -1)
    goto CleanUp7;
  if ((SelectDownSigNum = AllocSignal(-1)) == -1)
    goto CleanUp6;

 /****************************************
  * Build connection to the input.device *
  ****************************************/
  if (!(InputDevPort  = CreatePort(IMPDEVPORTNAME,0)))
    goto CleanUp5;
  if ((InputReqBlock = (struct IOStdReq *)
		       AllocMem(sizeof(struct IOStdReq),
		       MEMF_CLEAR | MEMF_PUBLIC)) == NULL)
    goto CleanUp4;

  InputReqBlock->io_Message.mn_Node.ln_Type = NT_MESSAGE;
  InputReqBlock->io_Message.mn_Length	    = sizeof(struct IOStdReq);
  InputReqBlock->io_Message.mn_ReplyPort    = InputDevPort;

  if (OpenDevice("input.device",0,(struct IORequest *)InputReqBlock,0))
    goto CleanUp3;

  /****************************************
   * Bulid connection to the timer.device *
   ****************************************/
  if (!(TimerPort = CreatePort(TIMERPORTNAME,0)))
    goto CleanUp3x3;
  if (!(TimerReqBlock = (struct timerequest *)
		       AllocMem(sizeof(struct timerequest),
		       MEMF_CLEAR | MEMF_PUBLIC)))
    goto CleanUp3x2;

  TimerReqBlock->tr_node.io_Message.mn_Node.ln_Type = NT_MESSAGE;
  TimerReqBlock->tr_node.io_Message.mn_Length	    = sizeof(struct timerequest);
  TimerReqBlock->tr_node.io_Message.mn_ReplyPort    = TimerPort;

  if (OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)TimerReqBlock,0))
    goto CleanUp3x1;

  /* Start Timer (just to be sure that at least one request is sent. */
  /*		  CheckIO doesn't work otherwise, i think) */

  QueueTimer();

  /********************
   * Make a semaphore *
   ********************/
  PopUpSemaphore.ss_Link.ln_Name = SEMAPHORENAME;
  PopUpSemaphore.ss_Link.ln_Pri  = 0;
  AddSemaphore(&PopUpSemaphore);

  /**************************************************
   * patch intuition functions to use our semaphore *
   **************************************************/
  if (!(OldSetMenuStrip = SetFunction((struct Library *)IntuitionBase,
				      LVOSetMenuStrip,MySetMenuStrip)))
    goto CleanUp2x4;
  if (!(OldClearMenuStrip = SetFunction((struct Library *)IntuitionBase,
					LVOClearMenuStrip,MyClearMenuStrip)))
    goto CleanUp2x3;
  if (!(OldOnMenu = SetFunction((struct Library *)IntuitionBase,
				LVOOnMenu,MyOnMenu)))
    goto CleanUp2x2;
  if (!(OldOffMenu = SetFunction((struct Library *)IntuitionBase,
				 LVOOffMenu,MyOffMenu)))
    goto CleanUp2x1;

  /**********************************
   * init data for the inputhandler *
   **********************************/
  InputSignals.PopUpMenuTask = FindTask(0);
  InputSignals.MenuUpSig     = 1L << MenuUpSigNum;
  InputSignals.MenuDownSig   = 1L << MenuDownSigNum;
  InputSignals.MouseMovedSig = 1L << MouseMovedSigNum;
  InputSignals.SelectDownSig = 1L << SelectDownSigNum;
  InputSignals.Down	     = FALSE;  /* menubutton is not down. (who cares) */

  /****************************
   * startup the inputhandler *
   ****************************/
  InputReqData.is_Node.ln_Pri  = INPHANDLPRI;	    /* must come before intuition */
  InputReqData.is_Node.ln_Name = INPHANDLNAME;

  InputReqData.is_Data	       = (APTR)&InputSignals;
  InputReqData.is_Code	       = (VOID *)PopUpHandler;

  InputReqBlock->io_Command = IND_ADDHANDLER;
  InputReqBlock->io_Data    = (APTR)&InputReqData;

  DoIO((struct IORequest *)InputReqBlock);

  /***************************************
   * tell the user that everything is ok *
   ***************************************/
  WriteAndClose(VERSION STARTMSG, sizeof(VERSION STARTMSG));

  PopUpMainLoop(&InputSignals,ReplyPort);

CleanUp1:

  /* remove inputhandler */
  InputReqBlock->io_Command = IND_REMHANDLER;
  InputReqBlock->io_Data    = (APTR)&InputReqData;

  DoIO((struct IORequest *)InputReqBlock);

  /* restore intuition functions */
  SetFunction((struct Library *)IntuitionBase,LVOOffMenu,OldOffMenu);
CleanUp2x1:
  SetFunction((struct Library *)IntuitionBase,LVOOnMenu,OldOnMenu);
CleanUp2x2:
  SetFunction((struct Library *)IntuitionBase,LVOClearMenuStrip,OldClearMenuStrip);
CleanUp2x3:
  SetFunction((struct Library *)IntuitionBase,LVOSetMenuStrip,OldSetMenuStrip);
CleanUp2x4:

  /* remove semaphore */
  RemSemaphore(&PopUpSemaphore);

  /* close timer.device */
  CloseDevice((struct IORequest *)TimerReqBlock);
CleanUp3x1:
  FreeMem(TimerReqBlock,sizeof(struct timerequest));
CleanUp3x2:
  DeletePort(TimerPort);
CleanUp3x3:

  /* close input.device */
  CloseDevice((struct IORequest *)InputReqBlock);
CleanUp3:
  DeleteStdIO(InputReqBlock);
CleanUp4:
  DeletePort(InputDevPort);
CleanUp5:

  /* Free allocated signals */
  FreeSignal(SelectDownSigNum);
CleanUp6:
  FreeSignal(MouseMovedSigNum);
CleanUp7:
  FreeSignal(MenuDownSigNum);
CleanUp8:
  FreeSignal(MenuUpSigNum);
CleanUp9:

  /* close libraries */
  CloseLibrary((struct Library *)LayersBase);
CleanUp10:
  CloseLibrary((struct Library *)GfxBase);
CleanUp11:
  CloseLibrary((struct Library *)IntuitionBase);
CleanUp12:
  DeletePort(ReplyPort);
CleanUp13:
  if (StdOut)
    WriteAndClose(FAILMSG,sizeof(FAILMSG));
  if (PopUpSeg) {
    /* if loaded from CLI unload us */
    Forbid();
    UnLoadSeg(PopUpSeg);
  }
  CloseLibrary((struct Library *)DOSBase);
}

/***************************************
 * WriteAndClose(Text,Length)          *
 *				       *
 * Input:			       *
 *   Text   - Text to write to StdOut. *
 *   Length			       *
 ***************************************/
VOID WriteAndClose(Text, Length)
  STRPTR Text;
  ULONG Length;
{
  IMPORT BPTR StdOut;

  Write(StdOut,Text,Length);
  Delay(DELAYTIME);
  Close(StdOut);
  StdOut = NULL;
}

/************************************************
 * PopUpMainLoop(InputSignals,ReplyPort)        *
 *						*
 * Input:					*
 *   InputSignals - Allocated signals.		*
 *   ReplyPort	  - Port for MENUVERIFY replies *
 * Output:					*
 *   none					*
 ************************************************/
VOID PopUpMainLoop(InputSignals,ReplyPort)
  struct SignalData *const InputSignals;
  struct MsgPort *const ReplyPort;
{
  IMPORT struct Window	*ActiveWindow;
  IMPORT struct Screen	*Screen;
  IMPORT struct Menu	*Menues;

  const LONGBITS ReplySig    = 1L << (LONG)ReplyPort->mp_SigBit;
  const LONGBITS MenuUpSig   = InputSignals->MenuUpSig;
  const LONGBITS MenuDownSig = InputSignals->MenuDownSig;
  const LONGBITS AllSignals  = ReplySig | MenuUpSig | MenuDownSig;

  WORD	NrOfMessages = 0;
  BOOL	QuitPopUpMenu = FALSE;
  BOOL	VerifyOK = FALSE;

  FOREVER {
    const LONGBITS SignalBits = Wait(AllSignals);

    if (SignalBits & MenuUpSig) {
      ActiveWindow = NULL;
      VerifyOK	   = FALSE;
    }

    if (SignalBits & ReplySig) {
      struct IntuiMessage *Message;

      while (Message = (struct IntuiMessage *)GetMsg(ReplyPort)) {
	if (Message->Class == MENUVERIFY) {
	  if (Message->IDCMPWindow == ActiveWindow)
	    VerifyOK = (Message->Code != MENUCANCEL);
	  NrOfMessages--;
	  FreeMem(Message,(LONG)Message->ExecMessage.mn_Length +
			      sizeof(struct Message));
	}
	else { /* Message->Class == QUITPOPUPMENU or some strange message */
	  ReplyMsg((struct Message *)Message); /* Message does not belong to this task */
	  QuitPopUpMenu = TRUE;
	}

      } /* while */
      if (NrOfMessages == 0) {
	if (QuitPopUpMenu)
	  break;
	if (VerifyOK) {
	  PopUpMenu(InputSignals);
	  VerifyOK = FALSE;
	}
      }
    }

    if ((SignalBits & (MenuUpSig | MenuDownSig)) == MenuDownSig) {
      const LONG  Lock = LockIBase(0);

      if ((ActiveWindow = IntuitionBase->ActiveWindow) AND
	 !(ActiveWindow->Flags & RMBTRAP) AND
	 (ActiveWindow->MenuStrip)) {
	Screen = ActiveWindow->WScreen;
	UnlockIBase(Lock);
	if ((NrOfMessages = SendMessage(ReplyPort)) == 0)
	  PopUpMenu(InputSignals);
	else
	  VerifyOK = TRUE;
      }
      else
	UnlockIBase(Lock);
    }
  } /* FOREVER */
}