/* * CALC.C Provides a calculator that opens on the active screen when * you press a specific key sequence. Otherwise, the program * waits quitely in the background. * * Copyright 1989 by Davide P. Cervone. * You may use this code, provided this copyright notice is kept intact. */ #include "cLoader.h" extern struct IntuitionBase *IntuitionBase; extern struct GfxBase *GfxBase; static char *program = PROGRAM; static char *copyright = COPYRIGHT; static char *handler = HANDLERCODE; /* The name of the handler file */ #define HANDLER &(handler[2]) /* Handler without the L: */ static struct HandlerData *cHandlerData; /* data shared with handler */ static long Segment; /* The loaded handler segment */ static struct Process *HandlerTask; /* The process created */ static struct MsgPort *InputPort; /* To talk to Input.Device */ static struct IOStdReq *InputBlock; /* IO block for Input.Device */ static int InputDevice; /* Is Input.Device open? */ static int VectorsSet; /* TRUE after SetFunction */ static int ProcStarted; /* TRUE acter CreateProc */ static UWORD KeyCode = 0x50; /* the keycode to activate Calc */ static UWORD Qualifiers = 0; /* and required qualifiers */ struct KeywordDef /* keywords and ther qualifiers */ { char *Name; UWORD Qualifier; }; static struct KeywordDef Keyword[] = { {"LSHIFT", IEQUALIFIER_LSHIFT}, {"RSHIFT", IEQUALIFIER_RSHIFT}, {"SHIFT", IEQUALIFIER_LSHIFT}, {"CAPSLOCK", IEQUALIFIER_CAPSLOCK}, {"CONTROL", IEQUALIFIER_CONTROL}, {"LALT", IEQUALIFIER_LALT}, {"RALT", IEQUALIFIER_RALT}, {"ALT", IEQUALIFIER_LALT}, {"LAMIGA", IEQUALIFIER_LCOMMAND}, {"RAMIGA", IEQUALIFIER_RCOMMAND}, {"AMIGA", IEQUALIFIER_LCOMMAND}, {"LCOMMAND", IEQUALIFIER_LCOMMAND}, {"RCOMMAND", IEQUALIFIER_RCOMMAND}, {NULL} }; /* * DoExit() * * General purpose error-exit routine. Print an error message if one was * supplied (it can have up to three parameters), and then clean up any * memory, libraries, etc. that need to be handled before exiting. */ static void DoExit(s,x1,x2,x3) char *s, *x1, *x2, *x3; { long status = EXIT_OK; ULONG signals; if (s != NULL) { printf(s,x1,x2,x3); printf("\n"); status = EXIT_ERROR; } if (VectorsSet) UnSetVectors(); if (InputDevice) CloseDevice(InputBlock); if (InputBlock) DeleteStdIO(InputBlock); if (InputPort) DeletePort(InputPort); if (ProcStarted) { Signal(HandlerTask,ENDSIGNAL); signals = Wait(ENDSIGNAL | SIGBREAKF_CTRL_C); if (signals & SIGBREAKF_CTRL_C) printf("CTRL-C detected!!!\n"); } if (Segment) UnLoadSeg(Segment); if (IntuitionBase) CloseLibrary(IntuitionBase); if (GfxBase) CloseLibrary(GfxBase); exit(status); } /* * CheckLibOpen() * * Call OpenLibrary() for the specified library, and check that the * open succeeded. */ static void CheckLibOpen(lib,name,rev) APTR *lib; char *name; int rev; { extern APTR OpenLibrary(); if ((*lib = OpenLibrary(name,(LONG)rev)) == NULL) DoExit("Can't open %s",name); } /* * CheckArguments() * * For each command-line argument: * Look through the keyword list for the named argument. * If no match found, then * Check to see if the argument is an F-key name ('F1','F2', etc). * If so, then set the KeyCode accordingly, * Otherwise, try to read a keycode number. * If unsuccessful, then error. * Otherwise (a keyword was found) * so add the requested qualifier to the required qualifier list. */ static void CheckArguments(argc,argv) int argc; char **argv; { short i; int NotFound; long Code; while (--argc) { argv++; for (i=0, NotFound=TRUE; Keyword[i].Name && NotFound; i++) NotFound = stricmp(*argv,Keyword[i].Name); if (NotFound) { if (((*argv)[0] == 'F' || (*argv)[0] == 'f') && (((*argv)[1] >= '1' && (*argv)[1] <= '9' && (*argv)[2] == 0) || ((*argv)[1] == '1' && (*argv)[2] == '0' && (*argv)[3] == 0))) { if ((*argv)[2] == 0) KeyCode = 0x50 + (*argv)[1] - '1'; else KeyCode = 0x59; } else { if (sscanf(*argv,"%x",&Code) != 1) DoExit("Unrecognized keyword '%s'",*argv); KeyCode = Code; } } else { Qualifiers ^= Keyword[i-1].Qualifier; } } } /* * LoadHandler() * * Try to LoadSeg the handler from the current directory, and if it is not * found, try the L: directory. If neither can be loaded, exit with an * error message. Once the handler is loaded, create a process from the * loaded segment (error if it could not be created) and set a flag so * that we can clean up if other errors occur. */ static void LoadHandler() { struct Process *theProcess; if ((Segment = LoadSeg(HANDLER)) == NULL) if ((Segment = LoadSeg(handler)) == NULL) DoExit("Can't load %s",handler); theProcess = CreateProc(HANDLERTASKNAME,0L,Segment,2048L); if (theProcess == NULL) DoExit("Can't Create Handler Process"); ProcStarted = TRUE; } /* * SetupHandler() * * Look for the handler task, and error if it can't be found. Get a message * port and a new StartupMessage structure to send the the Handler. Put * the loader version number in the StartupMessage so the Handler can check * whether it is OK (if not, it will return a NULL HandlerData pointer). * Send the message to the Handler, and wait for a reply. When it is * returned, get the HandlerData pointer, and free what we allocated. * If the HandlerData is NULL, then there was a version mismatch reported * by the Handler, otherwise, check the handler version to make sure we know * how to set it up. Finally, set thePort the the Handler Port value. */ static void SetupHandler(thePort) struct MsgPort **thePort; { struct StartupMessage *sMessage; struct MsgPort *rPort; ULONG signals; ULONG rSig; HandlerTask = FindTask(HANDLERTASKNAME); if (HandlerTask == NULL) DoExit("Can't Find Handler Task"); NEWPORT(rPort); if (NEWSTARTUP(sMessage)) { rSig = ONE << rPort->mp_SigBit; sMessage->sm_Message.mn_ReplyPort = rPort; sMessage->sm_HandlerData = NULL; sMessage->sm_ParentTask = (struct Task *)FindTask(NULL); sMessage->sm_LoadVers = LOADVERS; PutMsg(&(HandlerTask->pr_MsgPort),sMessage); signals = Wait(rSig | SIGBREAKF_CTRL_C); if (signals & SIGBREAKF_CTRL_C) DoExit("CTRL-C Detected!"); GETSTARTUP(rPort); cHandlerData = sMessage->sm_HandlerData; FREESTARTUP(sMessage); DeletePort(rPort); if (cHandlerData == NULL) DoExit("%s reports a version mismatch",HANDLER); if (var(MajVers) < MINHMAJVERS || (var(MajVers) == MINHMAJVERS && var(MinVers) < MINHMINVERS)) DoExit("Version mismatch with %s",HANDLER); *thePort = &(cHandlerData->HandlerPort); } else { DeletePort(rPort); DoExit("Can't Get Memory for Startup Message"); } } /* * StartHandler() * * Send the Handler the start signal so that it know everything is * set up for it. Clear the old ParentTask pointer, since the task * that eventually removes the process may not be the same as the one * that started it. */ void StartHandler() { Signal(HandlerTask,STARTSIGNAL); Forbid(); VAR(ParentTask) = NULL; Permit(); } /* * StopHandler() * * Send the Handler the stop signal, and wait for a reply (the Handler * may need to clean things up before we remove it). Be sure that the * ParentTask points to us, and not to the original loader task, which * may not be the same. */ void StopHandler() { ULONG signals; Forbid(); VAR(ParentTask) = (struct Task *)FindTask(NULL); Permit(); Signal(HandlerTask,ENDSIGNAL); signals = Wait(ENDSIGNAL | SIGBREAKF_CTRL_C); if (signals & SIGBREAKF_CTRL_C) DoExit("CTRL-C detected!!"); } /* * TellInputDevice() * * Create a port and I/O block, then open the input device. Set up the * I/O block to add or remove the input handler, and send the request * to the input device. Finally, close the device and delete the * I/O block and port. */ void TellInputDevice(function) int function; { long status; extern struct MsgPort *CreatePort(); extern struct IOStdReq *CreateStdIO(); if ((InputPort = CreatePort(0,0)) == NULL) DoExit("Can't Create Port"); if ((InputBlock = CreateStdIO(InputPort)) == NULL) DoExit("Can't Create Standard IO Block"); InputDevice = (OpenDevice("input.device",0,InputBlock,0) == 0); if (InputDevice == FALSE) DoExit("Can't Open 'input.device'"); InputBlock->io_Command = (long) function; InputBlock->io_Data = (APTR) var(Handler_Interrupt); if (status = DoIO(InputBlock)) DoExit("Error from DoIO: %ld",status); CloseDevice(InputBlock); InputDevice = FALSE; DeleteStdIO(InputBlock); InputBlock = NULL; DeletePort(InputPort); InputPort = NULL; } /* * SetVectors() * * Set the Intuition library vectors for the routines specified by the * handler. Save the old routine pointers for later replacement. */ void SetVectors() { if (var(aCloseScreen) && var(OldCloseScreen)) { VAR(OldCloseScreen) = SetFunction(IntuitionBase,&LVOCloseScreen,var(aCloseScreen)); VectorsSet = TRUE; } } /* * UnSetVectors() * * Replace the old Intuition library vectors, but make sure that no one * else has changed them behind our back. If they are not the same as * what we set them to originally, then put back the ones that we found, * and return an error status. */ int UnSetVectors() { long NewCloseScreen; int status = TRUE; if (var(aCloseScreen) && var(OldCloseScreen)) { Forbid(); NewCloseScreen = SetFunction(IntuitionBase,&LVOCloseScreen,VAR(OldCloseScreen)); if (NewCloseScreen != (long) var(aCloseScreen)) { SetFunction(IntuitionBase,&LVOCloseScreen,NewCloseScreen); status = FALSE; } Permit(); } return(status); } /* * SetVariables() * * The HandlerData structure is used to allow the loading program to * set up variables needed by the handler (like Intuitionbase, etc.). This * keeps the handler code to a minimum. The loader retains pointers to the * linked lists, in case it needs to free memory on behalf of the handler. */ void SetVariables(thePort) struct MsgPort *thePort; { VAR(IntuitionBase) = IntuitionBase; VAR(GfxBase) = GfxBase; var(Segment) = Segment; VAR(KeyCode) = KeyCode; VAR(Qualifiers) = Qualifiers; } /* * GetVariables() * * Look up the values stored in the HandlerData structure. The * Intuition library already was opened, and we will need to close it. * Similarly for the Graphics library. */ void GetVariables(thePort) struct MsgPort *thePort; { cHandlerData = (struct HandlerData *)thePort; IntuitionBase = VAR(IntuitionBase); GfxBase = VAR(GfxBase); } /* * Main() * * Look for the Handler Process. * If the process does not exist, then the Handler is not active, so: * Handle the arguments (if any). * Open libraries. * Load the handler code and create the process. * Send the startup message to the process and get the HandlerData. * Set the variables needed by the handler. * Set the library vectors for the handler routines. * Add the input handler into the Input Device chain. * Add the port (supplied by the handler) into the system list so we * can find it later. * Signal the process that all is OK. * Notify the user that all is ready. * else (the process already exists, so the Handler already is active) * Get the pointer to the HandlerData structure from the port, * and get any variables we need from the structure. * Check that the loader versions are compatible. * Remove the input handler from the Input Device chain * Signal the Handler to stop, and wait for reply. * Try to remove the SetFunction calls. * If successfull, then * Remove the port from the system list. * Signal Handler to end and wait for reply. * Unload the handler segment list. * Notify the user that the Handler is deactivated. * Close libraries. * else (we could not replace the functions) * Put back the input handler. * Signal the Handler to continue. * Inform the user that the Handler can not be removed. */ void main(argc,argv) int argc; char **argv; { struct MsgPort *NamedPort; HandlerTask = FindTask(HANDLERTASKNAME); if (HandlerTask == NULL) { if (argc > 1) CheckArguments(argc,argv); CheckLibOpen(&IntuitionBase,"intuition.library",INTUITION_REV); CheckLibOpen(&GfxBase,"graphics.library",GRAPHICS_REV); LoadHandler(); SetupHandler(&NamedPort); SetVariables(NamedPort); SetVectors(NamedPort); TellInputDevice(IND_ADDHANDLER); AddPort(NamedPort); StartHandler(); printf("%s v%d.%d.%d Installed\n",program, var(MajVers),var(MinVers),LOADVERS); } else { NamedPort = FindPort(PORTNAME); if (NamedPort == NULL) DoExit("Can't find port '%s'",PORTNAME); GetVariables(NamedPort); if (var(MinLoadVers) > LOADVERS || var(MajVers) < MINHMAJVERS || (var(MajVers) == MINHMAJVERS && var(MinVers) < MINHMINVERS)) { printf("Loader version mismatch\n"); printf("%s not removed\n",program); } else { TellInputDevice(IND_REMHANDLER); StopHandler(); if (UnSetVectors(NamedPort)) { RemPort(NamedPort); StopHandler(); UnLoadSeg(var(Segment)); printf("%s removed\n",program); CloseLibrary(IntuitionBase); CloseLibrary(GfxBase); } else { TellInputDevice(IND_ADDHANDLER); StartHandler(); printf("SetFunction vectors have been changed!\n"); printf("Cannot remove %s\n",program); } } } }