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