/* * tclMacInterupt.c -- * * This file contains routines that deal with the Macintosh's low level * time manager. This code provides a better resolution timer than what * can be provided by WaitNextEvent. * * Copyright (c) 1996 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tclMacInterupt.c,v 1.2 1998/09/14 18:40:05 stanton Exp $ */ #include "tclInt.h" #include "tclMacInt.h" #include #include #include /* * Data structure for timer tasks. */ typedef struct TMInfo { TMTask tmTask; ProcessSerialNumber psn; Point lastPoint; Point newPoint; long currentA5; long ourA5; int installed; } TMInfo; /* * Globals used within this file. */ static TimerUPP sleepTimerProc = NULL; static int interuptsInited = false; static ProcessSerialNumber applicationPSN; #define MAX_TIMER_ARRAY_SIZE 16 static TMInfo timerInfoArray[MAX_TIMER_ARRAY_SIZE]; static int topTimerElement = 0; /* * Prototypes for procedures that are referenced only in this file: */ #if !GENERATINGCFM static TMInfo * GetTMInfo(void) ONEWORDINLINE(0x2E89); /* MOVE.L A1,(SP) */ #endif static void SleepTimerProc _ANSI_ARGS_((void)); static pascal void CleanUpExitProc _ANSI_ARGS_((void)); static void InitInteruptSystem _ANSI_ARGS_((void)); /* *---------------------------------------------------------------------- * * InitInteruptSystem -- * * Does various initialization for the functions used in this * file. Sets up Universial Pricedure Pointers, installs a trap * patch for ExitToShell, etc. * * Results: * None. * * Side effects: * Various initialization. * *---------------------------------------------------------------------- */ void InitInteruptSystem() { int i; sleepTimerProc = NewTimerProc(SleepTimerProc); GetCurrentProcess(&applicationPSN); for (i = 0; i < MAX_TIMER_ARRAY_SIZE; i++) { timerInfoArray[i].installed = false; } /* * Install the ExitToShell patch. We use this patch instead * of the Tcl exit mechanism because we need to ensure that * these routines are cleaned up even if we crash or are forced * to quit. There are some circumstances when the Tcl exit * handlers may not fire. */ TclMacInstallExitToShellPatch(CleanUpExitProc); interuptsInited = true; } /* *---------------------------------------------------------------------- * * TclMacStartTimer -- * * Install a Time Manager task to wake our process up in the * future. The process should get a NULL event after ms * milliseconds. * * Results: * None. * * Side effects: * Schedules our process to wake up. * *---------------------------------------------------------------------- */ void * TclMacStartTimer( long ms) /* Milliseconds. */ { TMInfo *timerInfoPtr; if (!interuptsInited) { InitInteruptSystem(); } /* * Obtain a pointer for the timer. We only allocate up * to MAX_TIMER_ARRAY_SIZE timers. If we are past that * max we return NULL. */ if (topTimerElement < MAX_TIMER_ARRAY_SIZE) { timerInfoPtr = &timerInfoArray[topTimerElement]; topTimerElement++; } else { return NULL; } /* * Install timer to wake process in ms milliseconds. */ timerInfoPtr->tmTask.tmAddr = sleepTimerProc; timerInfoPtr->tmTask.tmWakeUp = 0; timerInfoPtr->tmTask.tmReserved = 0; timerInfoPtr->psn = applicationPSN; timerInfoPtr->installed = true; InsTime((QElemPtr) timerInfoPtr); PrimeTime((QElemPtr) timerInfoPtr, (long) ms); return (void *) timerInfoPtr; } /* *---------------------------------------------------------------------- * * TclMacRemoveTimer -- * * Remove the timer event from the Time Manager. * * Results: * None. * * Side effects: * A scheduled timer would be removed. * *---------------------------------------------------------------------- */ void TclMacRemoveTimer( void * timerToken) /* Token got from start timer. */ { TMInfo *timerInfoPtr = (TMInfo *) timerToken; if (timerInfoPtr == NULL) { return; } RmvTime((QElemPtr) timerInfoPtr); timerInfoPtr->installed = false; topTimerElement--; } /* *---------------------------------------------------------------------- * * TclMacTimerExpired -- * * Check to see if the installed timer has expired. * * Results: * True if timer has expired, false otherwise. * * Side effects: * None. * *---------------------------------------------------------------------- */ int TclMacTimerExpired( void * timerToken) /* Our token again. */ { TMInfo *timerInfoPtr = (TMInfo *) timerToken; if ((timerInfoPtr == NULL) || !(timerInfoPtr->tmTask.qType & kTMTaskActive)) { return true; } else { return false; } } /* *---------------------------------------------------------------------- * * SleepTimerProc -- * * Time proc is called by the is a callback routine placed in the * system by Tcl_Sleep. The routine is called at interupt time * and threrfor can not move or allocate memory. This call will * schedule our process to wake up the next time the process gets * around to consider running it. * * Results: * None. * * Side effects: * Schedules our process to wake up. * *---------------------------------------------------------------------- */ static void SleepTimerProc() { /* * In CFM code we can access our code directly. In 68k code that * isn't based on CFM we must do a glorious hack. The function * GetTMInfo is an inline assembler call that moves the pointer * at A1 to the top of the stack. The Time Manager keeps the TMTask * info record there before calling this call back. In order for * this to work the infoPtr argument must be the *last* item on the * stack. If we "piggyback" our data to the TMTask info record we * can get access to the information we need. While this is really * ugly - it's the way Apple recomends it be done - go figure... */ #if GENERATINGCFM WakeUpProcess(&applicationPSN); #else TMInfo * infoPtr; infoPtr = GetTMInfo(); WakeUpProcess(&infoPtr->psn); #endif } /* *---------------------------------------------------------------------- * * CleanUpExitProc -- * * This procedure is invoked as an exit handler when ExitToShell * is called. It removes the system level timer handler if it * is installed. This must be called or the Mac OS will more than * likely crash. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static pascal void CleanUpExitProc() { int i; for (i = 0; i < MAX_TIMER_ARRAY_SIZE; i++) { if (timerInfoArray[i].installed) { RmvTime((QElemPtr) &timerInfoArray[i]); timerInfoArray[i].installed = false; } } }