#include "PopUpMenu.h" #include "Version.h" #define REPLYPORTNAME "Reply" #define IMPDEVPORTNAME "ImpDev" #define TIMERPORTNAME "Timer" #define QUITPORTNAME "Quit" #define INPHANDLNAME "PopUpMenu" #define SEMAPHORENAME "PopUpMenu" #define POPUPMSG "\x9B0;33mPopUpMenu\x9B0m " #define KILLMSG "removed.\n" #define STARTMSG "installed.\n\xA9 Martin Adrian 1990\n" #define FAILMSG "failed.\n" /* Let the text stay for 3s if started from WorkBench */ #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 VOID InitPopUpMenu() { IMPORT struct DosLibrary *DosBase; IMPORT struct IntuitionBase *IntuitionBase; IMPORT struct GfxBase *GfxBase; IMPORT struct LayersBase *LayersBase; IMPORT struct IOStdReq *InputReqBlock; IMPORT struct MsgPort *TimerPort; IMPORT struct timerequest *TimerReqBlock; IMPORT LONGBITS TimerSignal; IMPORT struct MsgPort *ReplyPort; IMPORT struct SignalSemaphore PopUpSemaphore; IMPORT __fptr OldSetMenuStrip, OldClearMenuStrip, OldOnMenu, OldOffMenu; IMPORT BPTR StdOut; IMPORT BPTR PopUpSeg; struct MsgPort *InputDevPort; 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 = MyCreatePort(REPLYPORTNAME))) goto CleanUp13; } else { /* yes, popupmenues already installed, tell running task to quit */ struct MsgPort *const QuitPort = MyCreatePort(QUITPORTNAME); 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,sizeof(struct IntuiMessage)); WriteAndClose(KILLMSG, sizeof(KILLMSG)); } MyDeletePort(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 = MyCreatePort(IMPDEVPORTNAME))) 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 = MyCreatePort(TIMERPORTNAME))) goto CleanUp3x3; TimerSignal = 1L << TimerPort->mp_SigBit; 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) */ QueTimer(); /******************** * Make a semaphore * ********************/ PopUpSemaphore.ss_Link.ln_Name = /*SEMAPHORENAME*/ NULL; PopUpSemaphore.ss_Link.ln_Pri = 0; /* AddSemaphore(&PopUpSemaphore); */ InitSemaphore(&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); 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: MyDeletePort(TimerPort); CleanUp3x3: /* close input.device */ CloseDevice((struct IORequest *)InputReqBlock); CleanUp3: DeleteStdIO(InputReqBlock); CleanUp4: MyDeletePort(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: MyDeletePort(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) struct SignalData *const InputSignals; { IMPORT struct Window *ActiveWindow; IMPORT struct Screen *Screen; IMPORT struct Menu *Menues; IMPORT struct MsgPort *ReplyPort; const LONGBITS ReplySig = 1L << (LONG)ReplyPort->mp_SigBit; WORD NrOfMessages = 0; WORD Flags = 0; FOREVER { const LONGBITS SignalBits = Wait(ReplySig | InputSignals->MenuUpSig | InputSignals->MenuDownSig); if (SignalBits & InputSignals->MenuUpSig) { ActiveWindow = NULL; Flags &= QUIT; } if (SignalBits & ReplySig) { struct IntuiMessage *Message; while (Message = (struct IntuiMessage *)GetMsg(ReplyPort)) { if (Message->Class & MENUVERIFY) { if (Message->IDCMPWindow == ActiveWindow) if (Message->Code == MENUCANCEL) Flags &= QUIT; /* Verify not OK */ else Flags |= VERIFYOK; NrOfMessages--; FreeMem(Message,sizeof(struct IntuiMessage)); } else { /* Message->Class == QUITPOPUPMENU or some strange message */ ReplyMsg((struct Message *)Message); /* Message does not belong to this task */ Flags |= QUIT; } } /* while */ if (NrOfMessages == 0) { if (Flags & QUIT) break; if (Flags & VERIFYOK) { PopUpMenu(InputSignals); Flags = 0; } } } if ((SignalBits & (InputSignals->MenuUpSig | InputSignals->MenuDownSig)) == InputSignals->MenuDownSig) { const LONG Lock = LockIBase(0); ActiveWindow = IntuitionBase->ActiveWindow; if (ActiveWindow AND !(ActiveWindow->Flags & RMBTRAP) AND (ActiveWindow->MenuStrip)) { Screen = ActiveWindow->WScreen; NrOfMessages = SendMessage(); UnlockIBase(Lock); if (NrOfMessages == 0) PopUpMenu(InputSignals); else Flags |= VERIFYOK; } else UnlockIBase(Lock); } } /* FOREVER */ } /******************************************************* * SendMessage() - Send MENUVERIFY message * * to all windows on screen with MENUVERIFY flag set. * * IBase must be locked!! * * Input: * * none * * Output: * * return - Messages sent. * *******************************************************/ WORD SendMessage() { IMPORT struct Window *const ActiveWindow; IMPORT struct MsgPort *ReplyPort; IMPORT struct Screen *Screen; struct Window *Window; WORD NrOfMessages = 0; Window = Screen->FirstWindow; do { if (Window->IDCMPFlags & MENUVERIFY) { struct IntuiMessage *const Message = BuildIntuiMsg(ReplyPort, MENUVERIFY, (Window == ActiveWindow) ? MENUHOT : MENUWAITING); if (Message) { CurrentTime(&Message->Seconds,&Message->Micros); Message->IDCMPWindow = Window; PutMsg(Window->UserPort,(struct Message *)Message); NrOfMessages++; } } } while (Window = Window->NextWindow); return (NrOfMessages); } /*************************************** * BuildIntuiMsg(ReplyMsg,Class,Code) * * * * Input: * * ReplyPort * * Class * * Code * * Output: * * return - IntuiMessage * ***************************************/ struct IntuiMessage *BuildIntuiMsg(ReplyPort,Class,Code) struct MsgPort *const ReplyPort; ULONG Class; UWORD Code; { struct IntuiMessage *const Message = AllocMem(sizeof(struct IntuiMessage),MEMF_PUBLIC | MEMF_CLEAR); if (Message) { Message->ExecMessage.mn_Node.ln_Type = NT_MESSAGE; Message->ExecMessage.mn_ReplyPort = ReplyPort; Message->ExecMessage.mn_Length = sizeof(struct IntuiMessage) - sizeof(struct Message); Message->Class = Class; Message->Code = Code; } return (Message); } /******************************* * MyCreatePort(Name, Pri) * * MyDeletePort(Port) * * * * Replacements for amiga.lib * *******************************/ struct MsgPort *MyCreatePort(Name) STRPTR Name; { UBYTE SigBit; if ((SigBit = AllocSignal(-1)) != -1) { struct MsgPort *const Port = AllocMem(sizeof(struct MsgPort), MEMF_CLEAR | MEMF_PUBLIC); if (Port) { Port->mp_Node.ln_Name = Name; Port->mp_Node.ln_Pri = 0; Port->mp_Node.ln_Type = NT_MSGPORT; Port->mp_Flags = PA_SIGNAL; Port->mp_SigBit = SigBit; Port->mp_SigTask = (struct Task *)FindTask(0); AddPort(Port); return(Port); } else FreeSignal((LONG)SigBit); } return(NULL); } VOID MyDeletePort(Port) struct MsgPort *const Port; { RemPort(Port); FreeSignal((LONG)Port->mp_SigBit); FreeMem(Port,sizeof(struct MsgPort)); }