/**************************************************************************** * * KeyMacro.Handler.c ---- KeyMacro handler. * * Author ---------------- Olaf Barthel, MXM * Brabeckstrasse 35 * D-3000 Hannover 71 * * KeyMacro © Copyright 1990 by MXM; Executable program, * documentation and source code are shareware. If you like * this program a small donation will entitle you to receive * updates and new programs from MXM. * ****************************************************************************/ /* Function prototypes. */ VOID FreeString(BPTR); BPTR CreateBSTR(char *); BPTR CopyPath(VOID); VOID FakeCLI(VOID); VOID ClearPath(BPTR); VOID StopFakery(VOID); VOID * DeleteMacroMsg(struct MacroMessage *); VOID * SendMacroMsg(struct MacroMessage *,struct MsgPort *); VOID Executor(VOID); struct InputEvent * EventHandler(struct InputEvent *); struct MacroKey * FindMacroKey(LONG,LONG); LONG CreateHandler(VOID); VOID DeleteHandler(VOID); LONG _main(VOID); /* The magic stuff. */ #pragma regcall(EventHandler(a0)) /* Shared library identifiers. */ struct MXMBase *MXMBase; struct IntuitionBase *IntuitionBase; struct Library *ConsoleDevice; extern struct ExecBase *SysBase; /* Global handshake data. */ struct MSeg *MSeg; struct KeyEquivalent *EquList; /* Process<->Process communication data. */ struct Process *ExecuteProc; struct MsgPort *ExecutePort; /* Input device data. */ struct MsgPort *InputDevPort; struct IOStdReq *InputRequestBlock; struct Interrupt *InputHandler; struct InputEvent *FakeInputEvent; /* _main(): * * This is the entry point to the handler process. */ LONG _main() { struct Process *ThatsMe; ULONG SignalSet; struct MacroMessage *MacroMsg; LONG SigBit; LONG i; /* Do I know myself? */ ThatsMe = (struct Process *)SysBase -> ThisTask; /* Don't let anybody call us from CLI. */ if(ThatsMe -> pr_CLI) goto Quit; /* Can we find the global MsgPort? */ if(!(MSeg = (struct MSeg *)FindPort(PORTNAME))) { Forbid(); Signal(MSeg -> Father,MSeg -> RingBack); goto Quit; } /* This older revision probably doesn't support * some newer structure tags -> exit. */ if(MSeg -> Revision < REVISION) { Forbid(); Signal(MSeg -> Father,MSeg -> RingBack); goto Quit; } /* The MsgPort is already owned by someone * else. */ if(MSeg -> Port . mp_Flags & PA_SIGNAL) { Forbid(); Signal(MSeg -> Father,MSeg -> RingBack); goto Quit; } /* Open mxm.library. */ if(!(MXMBase = (struct MXMBase *)OpenLibrary("mxm.library",34))) { Forbid(); Signal(MSeg -> Father,MSeg -> RingBack); goto Quit; } /* Initialize the input.device handler. */ if(!CreateHandler()) { DeleteHandler(); CloseLibrary((struct Library *)MXMBase); Forbid(); Signal(MSeg -> Father,MSeg -> RingBack); goto Quit; } /* Extract shared library identifiers. */ IntuitionBase = (struct IntuitionBase *)MXMBase -> IntuitionBase; ConsoleDevice = (struct Library *)MXMBase -> ConsoleDevice; /* Allocate a fake InputEvent structure. */ if(!(FakeInputEvent = (struct InputEvent *)AllocRem(sizeof(struct InputEvent),MEMF_PUBLIC))) { DeleteHandler(); CloseLibrary((struct Library *)MXMBase); Forbid(); Signal(MSeg -> Father,MSeg -> RingBack); goto Quit; } /* Allocate a signal bit. */ if((SigBit = AllocSignal(-1)) == -1) { FreeRem(FakeInputEvent); DeleteHandler(); CloseLibrary((struct Library *)MXMBase); Forbid(); MSeg -> Child = NULL; Signal(MSeg -> Father,MSeg -> RingBack); goto Quit; } /* Start the executing process. */ MSeg -> Child = (struct Task *)ThatsMe; if(!(ExecuteProc = (struct Process *)CreateFuncProc("KeyMacro.exec",10,Executor,4000))) { FreeSignal(SigBit); FreeRem(FakeInputEvent); DeleteHandler(); CloseLibrary((struct Library *)MXMBase); Forbid(); MSeg -> Child = NULL; Signal(MSeg -> Father,MSeg -> RingBack); goto Quit; } /* Wait for handshake signal. */ Wait(SIG_SHAKE); /* Process creation failed. */ if(!ExecuteProc) { FreeSignal(SigBit); FreeRem(FakeInputEvent); DeleteHandler(); CloseLibrary((struct Library *)MXMBase); Forbid(); MSeg -> Child = NULL; Signal(MSeg -> Father,MSeg -> RingBack); goto Quit; } /* Now we are truly running. */ Signal(MSeg -> Father,MSeg -> RingBack); MSeg -> Father = NULL; /* Re-init the MsgPort flags. */ MSeg -> Port . mp_Flags = PA_SIGNAL; MSeg -> Port . mp_SigBit = SigBit; MSeg -> Port . mp_SigTask = MSeg -> Child; /* Wait until somebody kicks us out. */ FOREVER { SignalSet = Wait(SIG_CLOSE | SIG_PORT); /* We are to shut down. */ if(SignalSet & SIG_CLOSE) { FreeSignal(SigBit); FreeRem(FakeInputEvent); DeleteHandler(); if(ExecuteProc) { Signal((struct Task *)ExecuteProc,SIG_CLOSE); Wait(SIG_SHAKE); } CloseLibrary((struct Library *)MXMBase); Forbid(); Signal(MSeg -> Father,SIG_CLOSE); goto Quit; } /* A message arrived at our home port. */ if(SignalSet & SIG_PORT) { /* Walk through the list of messages. */ while(MacroMsg = (struct MacroMessage *)GetMsg(&MSeg -> Port)) { /* Execute a keyboard macro. */ if(MacroMsg -> mm_Type == MM_INPUT) { struct MacroKey *TempMacroKey = MacroMsg -> mm_MacroKey; if(TempMacroKey) { /* Let the execute process run the command. */ if(TempMacroKey -> mk_Type == MK_COMMAND) { struct MacroMessage CommandMsg; CommandMsg . mm_Type = MM_EXECUTE; CommandMsg . mm_FileName = TempMacroKey -> mk_String; CommandMsg . mm_WindowName = TempMacroKey -> mk_Window; SendMacroMsg(&CommandMsg,ExecutePort); } /* Build a keyboard macro. */ if(TempMacroKey -> mk_Type == MK_WORD) { InputRequestBlock -> io_Command = IND_WRITEEVENT; InputRequestBlock -> io_Data = (APTR)FakeInputEvent; memset(FakeInputEvent,0,sizeof(struct InputEvent)); FakeInputEvent -> ie_Class = IECLASS_RAWKEY; for(i = 0 ; i < strlen(TempMacroKey -> mk_String) ; i++) if(InvertKey(TempMacroKey -> mk_String[i],FakeInputEvent,IK_USEIKM,NULL)) DoIO(InputRequestBlock); } } } /* This is a request to update the * macro keys. */ if(MacroMsg -> mm_Type == MM_UPDATE) { if(MSeg -> MacroList) { for(i = 0 ; i < MSeg -> NumMacros ; i++) { if(MSeg -> MacroList[i] . mk_Type == MK_UNUSED) continue; if(MSeg -> MacroList[i] . mk_String) FreeRem(MSeg -> MacroList[i] . mk_String); if(MSeg -> MacroList[i] . mk_Window) FreeRem(MSeg -> MacroList[i] . mk_Window); } FreeRem(MSeg -> MacroList); } MSeg -> NumMacros = MacroMsg -> mm_NumMacros; MSeg -> MacroList = MacroMsg -> mm_MacroList; } /* Remove the message. */ DeleteMacroMsg(MacroMsg); } } } Quit: ; } /* FreeString(Byte): * * Frees the memory occupied by the contents of a BSTR. */ VOID FreeString(BPTR Byte) { LONG *Ptr = (LONG *)BADDR(Byte); FreeMem(Ptr - 1,Ptr[-1]); } /* CreateBSTR(s): * * Allocates enough memory to hold the contents of * a given string and makes it a BSTR. */ BPTR CreateBSTR(char *s) { LONG Length = strlen(s); LONG BlockLength = (Length + 8) & ~3; char *Byte; if(!(Byte = (char *)AllocMem(BlockLength,MEMF_PUBLIC | MEMF_CLEAR))) return(NULL); *(LONG *)Byte = BlockLength; Byte[4] = Length; strncpy(Byte + 5,s,Length); return((LONG)(Byte + 4) >> 2); } /* CopyPath(): * * Builds a fake pathlist inherited from any valid * CLI process or Workbench. */ BPTR CopyPath() { struct Process *Father; struct CommandLineInterface *CLI; BPTR *Next1,*Next2,*Last,NewPath = NULL; Last = &NewPath; /* If using ARP this will also give us a valid * pathlist. */ if(!(Father = (struct Process *)FindTask("Workbench"))) if(!(Father = (struct Process *)FindTask("ARP Shell Process"))) if(!(Father = (struct Process *)FindTask("New CLI"))) if(!(Father = (struct Process *)FindTask("Initial CLI"))) return(NULL); if(!(CLI = (struct CommandLineInterface *)BADDR(Father -> pr_CLI))) return(NULL); for(Next1 = (BPTR *)BADDR(CLI -> cli_CommandDir) ; Next1 ; Next1 = (BPTR *)BADDR(*Next1)) { if(!(Next2 = (BPTR *)AllocMem(2 * sizeof(BPTR),MEMF_PUBLIC | MEMF_CLEAR))) break; *Last = (LONG)Next2 >> 2; Last = Next2; Next2[1] = (BPTR)DupLock(Next1[1]); Next2[0] = NULL; } return(NewPath); } /* FakeCLI(): * * Creates a fake CLI structure for our process. This * includes pathlist, currentdir, prompt and stack. */ VOID FakeCLI() { struct CommandLineInterface *CLI; struct Process *MyProcess = (struct Process *)SysBase -> ThisTask; if(!(CLI = (struct CommandLineInterface *)AllocMem(sizeof(struct CommandLineInterface),MEMF_PUBLIC | MEMF_CLEAR))) return; MyProcess -> pr_CLI = (LONG)CLI >> 2; CLI -> cli_SetName = CreateBSTR("SYS:"); CLI -> cli_Prompt = CreateBSTR("%N> "); CLI -> cli_DefaultStack = 4000; CurrentDir(Lock("SYS:",ACCESS_READ)); CLI -> cli_CommandDir = CopyPath(); } /* ClearPath(InitPath): * * Frees the contents of our fake pathlist. */ VOID ClearPath(BPTR InitPath) { BPTR *Next,*Path; for(Path = (BPTR *)BADDR(InitPath) ; Path ; Path = Next) { Next = (BPTR *)BADDR(Path[0]); if(Path[1]) UnLock(Path[1]); FreeMem(Path,2 * sizeof(BPTR)); } } /* StopFakery(): * * Removes the contents of our fake CLI structure. */ VOID StopFakery() { BPTR MyCD = (BPTR)CurrentDir(NULL); struct Process *MyProcess = (struct Process *)SysBase -> ThisTask; struct CommandLineInterface *CLI = (struct CommandLineInterface *)BADDR(MyProcess -> pr_CLI); if(!CLI) return; if(MyCD) UnLock(MyCD); FreeString(CLI -> cli_SetName); FreeString(CLI -> cli_Prompt); ClearPath(CLI -> cli_CommandDir); MyProcess -> pr_CLI = NULL; FreeMem(CLI,sizeof(struct CommandLineInterface)); } /* DeleteMacroMsg(scm_Msg): * * Remove a message from memory. */ VOID * DeleteMacroMsg(struct MacroMessage *scm_Msg) { if(scm_Msg && scm_Msg -> mm_Message . mn_Node . ln_Name == (char *)scm_Msg) FreeRem(scm_Msg); return(NULL); } /* SendMacroMsg(scm_Msg,scm_Port): * * Post a cloned macro message to a MsgPort. */ VOID * SendMacroMsg(struct MacroMessage *scm_Msg,struct MsgPort *scm_Port) { struct MacroMessage *scm_TempMsg = (struct MacroMessage *)AllocRem(sizeof(struct MacroMessage),MEMF_PUBLIC | MEMF_CLEAR); if(scm_TempMsg) { CopyMem(scm_Msg,scm_TempMsg,sizeof(struct MacroMessage)); scm_TempMsg -> mm_Message . mn_Node . ln_Name = (char *)scm_TempMsg; scm_TempMsg -> mm_Message . mn_ReplyPort = NULL; scm_TempMsg -> mm_Message . mn_Length = sizeof(struct MacroMessage); PutMsg(scm_Port,(struct Message *)scm_TempMsg); } return((VOID *)scm_TempMsg); } /* Executor(): * * This is the dummy process to execute programs. */ VOID Executor() { ULONG SignalSet; BPTR NIL; struct MacroMessage *ExecuteMsg; struct Window *TheWindow; char TempLine[300]; geta4(); /* Try to allocate a port (we can't use our builtin * DOS port since we are actually calling DOS * routines which may mix up the messages coming * in). */ if(!(ExecutePort = (struct MsgPort *)CreatePort(NULL,0))) { Forbid(); ExecuteProc = NULL; Signal(MSeg -> Child,SIG_SHAKE); goto Quit; } /* Open the NULL-Handler. */ if(!(NIL = Open("NULL:",MODE_NEWFILE))) { Forbid(); ExecuteProc = NULL; Signal(MSeg -> Child,SIG_SHAKE); DeletePort(ExecutePort); goto Quit; } /* Pretend to be a CLI. */ FakeCLI(); { struct Process *ThatsMe = (struct Process *)SysBase -> ThisTask; /* These are inherited from the father process, * we had better cleared them out. */ ThatsMe -> pr_ConsoleTask = (APTR)DeviceProc("NULL:"); ThatsMe -> pr_WindowPtr = (APTR)-1; /* This path leads nowhere. */ ThatsMe -> pr_CIS = NIL; ThatsMe -> pr_COS = NIL; } /* We're on the scene now. */ Signal(MSeg -> Child,SIG_SHAKE); FOREVER { SignalSet = Wait(SIG_CLOSE | (1 << ExecutePort -> mp_SigBit)); /* Shut down? */ if(SignalSet & SIG_CLOSE) { StopFakery(); Close(NIL); DeletePort(ExecutePort); ExecuteProc = NULL; Forbid(); Signal(MSeg -> Child,SIG_SHAKE); goto Quit; } /* Execute a command? */ while(ExecuteMsg = (struct MacroMessage *)GetMsg(ExecutePort)) { TheWindow = NULL; /* Try to find a matching window title. */ if(ExecuteMsg -> mm_WindowName) { ULONG IntuiLock; struct Screen *ExScreen; struct Window *ExWindow; IntuiLock = LockIBase(NULL); ExScreen = IntuitionBase -> FirstScreen; do { ExWindow = ExScreen -> FirstWindow; do { if(!UStrCmp(ExecuteMsg -> mm_WindowName,ExWindow -> Title)) { UnlockIBase(IntuiLock); TheWindow = ExWindow; goto SkipLoop; } } while(ExWindow = ExWindow -> NextWindow); } while(ExScreen = ExScreen -> NextScreen); UnlockIBase(IntuiLock); } /* No chance, execute the command. */ strcpy(TempLine,"C:Run NULL: "); strcat(TempLine,ExecuteMsg -> mm_FileName); Execute(TempLine,NULL,NIL); SkipLoop: DeleteMacroMsg(ExecuteMsg); /* Found a window? Bring it to the front. */ if(TheWindow) { WindowToFront(TheWindow); ScreenToFront(TheWindow -> WScreen); ActivateWindow(TheWindow); } } } /* Finished, fall through. */ Quit: ; } /* FindMacroKey(Code,Qualifier): * * Find a macro key entry in the linked list of * macro key structures. */ struct MacroKey * FindMacroKey(LONG Code,LONG Qualifier) { LONG i; if(!MSeg -> MacroList) return(NULL); for(i = 0 ; i < MSeg -> NumMacros ; i++) { if(MSeg -> MacroList[i] . mk_Type == MK_UNUSED) continue; if(MSeg -> MacroList[i] . mk_CommandKey == Code && (Qualifier & MSeg -> MacroList[i] . mk_CommandQualifier) == MSeg -> MacroList[i] . mk_CommandQualifier) return(&MSeg -> MacroList[i]); } return(NULL); } /* EventHandler(Event): * * The input event handler. */ struct InputEvent * EventHandler(struct InputEvent *Event) { struct MacroKey *HandlerKey; /* This is an interrupt, let's start with the register * saving. */ int_start(); if(Event -> ie_Class == IECLASS_RAWKEY && !(Event -> ie_Code & IECODE_UP_PREFIX)) { if(HandlerKey = (struct MacroKey *)FindMacroKey(Event -> ie_Code,Event -> ie_Qualifier)) { struct MacroMessage HandlerMsg; HandlerMsg . mm_Type = MM_INPUT; HandlerMsg . mm_MacroKey= HandlerKey; SendMacroMsg(&HandlerMsg,&MSeg -> Port); Event -> ie_Class = IECLASS_NULL; } } /* Restore the registers. */ int_end(); return(Event); } /* CreateHandler(): * * Initialize the input event handler. */ LONG CreateHandler() { /* The basic initialization. */ if(!(InputDevPort = (struct MsgPort *)CreatePort(NULL,0))) return(FALSE); if(!(InputRequestBlock = (struct IOStdReq *)CreateStdIO(InputDevPort))) return(FALSE); if(OpenDevice("input.device",0,(struct IORequest *)InputRequestBlock,0)) return(FALSE); if(!(InputHandler = (struct Interrupt *)AllocRem(sizeof(struct Interrupt),MEMF_PUBLIC | MEMF_CLEAR))) return(FALSE); /* Build the input handler. */ InputHandler -> is_Code = (VOID *)EventHandler; InputHandler -> is_Node . ln_Pri = 51; InputHandler -> is_Node . ln_Name = "KeyMacro-Handler"; /* Add the handler. */ InputRequestBlock -> io_Command = IND_ADDHANDLER; InputRequestBlock -> io_Data = (APTR)InputHandler; if(DoIO((struct IORequest *)InputRequestBlock)) return(FALSE); return(TRUE); } /* DeleteHandler(): * * Remove the input event handler. */ VOID DeleteHandler() { /* Remove the input handler. */ if(InputRequestBlock) { if(InputRequestBlock -> io_Device) { InputRequestBlock -> io_Command = IND_REMHANDLER; InputRequestBlock -> io_Data = (APTR)InputHandler; DoIO((struct IORequest *)InputRequestBlock); CloseDevice((struct IORequest *)InputRequestBlock); } DeleteStdIO(InputRequestBlock); } /* Free the last memory. */ if(InputHandler) FreeRem(InputHandler); if(InputDevPort) DeletePort(InputDevPort); }