boot3/ProcessMgr/DeskMgrPatches.c
Elliot Nunn 5b0f0cc134 Bring in CubeE sources
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included.

The Tools directory, containing mostly junk, is also excluded.
2017-12-26 10:02:57 +08:00

826 lines
27 KiB
C

/*
File: DeskMgrPatches.c
Contains: Routines that patch the Desk Manager
Written by: Erich Ringewald
Copyright: © 1986-1991, 1993 by Apple Computer, Inc., all rights reserved.
Change History (most recent first):
<SM21> 7/21/93 joe Backed out <SM20>.
<19> 11/17/91 YK Modify code for the Text Services Manager in c_systemevent to
support apps which set SEvtEnb flag off.
<18> 11/1/91 DTY Use the system override map in FILE_NOT_IN_SYSTEM_MAP instead of
SYSMAPHANDLE.
<17> 10/15/91 YK Added code for the Text Services Manager in c_systemevent.
<16> 3/7/91 DFH stb, #84365: Fixed CoreLaunchDeskAccessory to deal with file
spec ptr == nil and driver name ptr == nil. Was calling
OpenDeskAcc ROM with nil name pointer.
<15> 3/7/91 DFH stb, #84195: Tuned sleep limiters to have a floor driver delay.
Greedy drivers were zeroing out the sleep period, causing the
front application to hog the CPU from the other applications.
<14> 3/5/91 DFH dnf, WS#DFH-910305a: Fixed LaunchDeskAccessory so that it can be
called when the DA itself is the current process.
<13> 1/28/91 DFH JSM,#81425: Removed inhibit_DAs mechanism that used to prevent
SystemEvent and SystemTask call-throughs while in MenuSelect.
There was no need for it, and it caused problems for drivers
during prolonged SystemMenu (e.g. Help menu's ModalDialog).
<12> 1/21/91 DFH (KSM) Process Menu is now called Application Menu.
<10> 1/8/91 DFH (stb) Fixed LimitFrontSleepForDriversSake and
LimitBackSleepForDriversSake to work correctly when dCtlDelay is
0.
<9> 1/8/91 DFH (ewa) Fixed CoreLaunchDeskAccessory() bug where retval was not
being set.
<8> 12/18/90 DFH Changed SystemEvent patch to give high-level debugger key even
if that debugger is pCurrentProcess.
<7> 12/17/90 DFH Rename CheckForCutCopy to CheckForCutCopyMenuKey.
<6> 12/14/90 DFH Added SynchIdleTime call in c_systemtask.
<5> 12/10/90 DFH Compiler bug workaround in c_sysedit. Use SCRAPCOUNT += 1
instead of SCRAPCOUNT++.
<4> 12/5/90 DFH Driver scheduling now affects application sleep time, not vice
versa.
<3> 11/30/90 DFH Fixed PEntryFromFileSpec call in CoreLaunchDeskAccessory to pass
pDASpec->daFileSpec instead of &fileSpec. This bug prevented
LaunchDeskAccessory from bringing forward an already open DA.
<0> 9/2/86 ELR New Today.
*/
#include <types.h>
#include <errors.h>
#include <memory.h>
#include <osutils.h>
#include <toolutils.h>
#include <quickdraw.h>
#include <fonts.h>
#include <events.h>
#include <desk.h>
#include <resources.h>
#include <retrace.h>
#include <menus.h>
#include <windows.h>
#include <dialogs.h>
#include <files.h>
#include <segload.h>
#include <devices.h>
#include <sound.h>
#include <MFPrivate.h>
#include <MenuMgrPriv.h>
#include <ResourceMgrPriv.h>
#include "Glue.h"
#include "Lomem.h"
#include "Data.h"
#include "SysMisc.h"
#include "Patches.h"
#include "Puppet.h"
#include "OSDispatchPrivate.h"
/* Some function prototypes that should be in (yet another) header file */
Boolean OptionKeyIsDown(void);
Handle GetMapHdlFromRefNum(short, Handle);
pascal OSErr CoreLaunchDeskAccessory(DASpecBlock *, short *, Boolean);
AppleItemHdl StringToAppleItemHdl(StringPtr);
void SendAppleMenuSelectMsg(AppleItemHdl);
PEntryPtr PEntryFromApplicationMenuItem(unsigned short);
void AdjustApplicationMenu(PEntryPtr);
void GiveDriversTime(void);
OSErr GetDrvrResourceAndInfo(StringPtr, Handle *, short *, Str255 *);
Boolean imLayerKeyDown(EventRecord *, Boolean);
/* Function prototypes internal to this file */
void GiveLocalDriversTime(void);
/* The macro below is used because Suitcase sticks maps below the system */
#define FILE_NOT_IN_SYSTEM_MAP(refNum) (GetMapHdlFromRefNum(refNum, GetOverrideMap(SYSMAPHANDLE)) == nil)
/* Used as floor value when adjusting application sleep time based on driver needs.
* Smaller values cause the application to sleep less, which robs time from the
* other applications. Larger values may impinge on real driver requirements.
* NOTE: The initial value of this (7 Mar 91) is just an educated guess.
*/
#define MIN_DRIVER_DELAY (5)
/* GetDrvrResourceAndInfo. Utility to get information about DA in current chain.
* Assumes a5 == PROCESSMGRGLOBALS
*/
OSErr
GetDrvrResourceAndInfo(StringPtr pDAName, Handle *phRsrc, short *theID, Str255 *theName)
{
register Handle drvrHdl;
/* Locate the DRVR resource */
drvrHdl = (pDAName != nil)
? CALL_FNC_PTR(Getnamedresource_ptr,patchtraps[GETNAMEDRESOURCE].oldtrap,('DRVR', pDAName))
: Get1IndResource('DRVR', 1);
/* Determine the file and resource refnums */
if (drvrHdl != nil)
{
ROMMAPINSERT = (-1);
GetResInfo(drvrHdl, theID, nil, theName);
}
*phRsrc = drvrHdl;
return(RESERR);
}
/* CheckUnitTableEntry. See if the UNITTABLE entry that this driver would occupy
* is acceptable. For now, just checks whether entry is already in use.
*/
OSErr
CheckUnitTableEntry(short resourceID)
{
register DCtlPtr dCtlPtr;
DCtlHandle dCtlHdl;
OSErr result;
result = noErr;
dCtlHdl = (DCtlHandle) UNITTABLE[resourceID];
if ( (dCtlHdl != nil) && ((dCtlPtr = *dCtlHdl) != nil) && ((dCtlPtr->dCtlFlags & DOpened) != 0) )
result = portInUse;
return(result);
}
pascal OSErr
CoreLaunchDeskAccessory(DASpecBlock *daSpecAddr, short *pDrvrNum, Boolean fromOpenDeskAcc)
{
register DASpecBlock *pDASpec;
register StringPtr pDAName;
register OSErr retval;
short daFile;
short daResourceNum;
Handle drvrHdl;
Boolean alreadyLaunched;
unsigned long olda5;
FSSpec fileSpec;
FCBPBRec fcbInfo;
LaunchResults dahLaunchResults;
Str255 rsrcName;
pascal void (*oldtrap)();
#define daOwnerPtr (dahLaunchResults.pNewProcess)
retval = noErr;
olda5 = ProcessMgrA5Setup();
daOwnerPtr = nil;
pDASpec = daSpecAddr;
pDAName = pDASpec->daDrvrName;
daResourceNum = 0;
/* daFileSpec == nil means that the resource MUST be in the existing resource chain.
* Locate the resource to determine the full file specification. Allow user to
* open the DA in the current layer if the option key is down.
*/
if (pDASpec->daFileSpec == nil)
{
/* Locate the DRVR resource */
if ((retval = GetDrvrResourceAndInfo(pDAName, &drvrHdl, &daResourceNum, &rsrcName)) != noErr)
goto ErrorRecovery;
/* Get file spec of resource file */
if ((daFile = HomeResFile(drvrHdl)) == 0)
daFile = SYSMAP;
fcbInfo.ioFCBIndx = 0;
fcbInfo.ioRefNum = daFile;
fcbInfo.ioNamePtr = &fileSpec.name;
fcbInfo.ioCompletion = nil;
if ((retval = PBGetFCBInfo(&fcbInfo, SyncHFS)) != noErr)
goto ErrorRecovery;
fileSpec.vRefNum = fcbInfo.ioFCBVRefNum;
fileSpec.parID = fcbInfo.ioFCBParID;
pDASpec->daFileSpec = &fileSpec;
/* See whether current process should get it */
if ( (FILE_NOT_IN_SYSTEM_MAP(daFile) || OptionKeyIsDown() ) &&
((pCurrentProcess->p_taskmode & modeDeskAccessory) == 0) )
{
if (pCurrentProcess->p_layer == nil)
{
retval = appIsDaemon;
goto ErrorRecovery;
}
else
daOwnerPtr = pCurrentProcess;
}
}
/* See if handler already launched. Must do this with a possibly nil pDAName,
* since we can't look in the file to find the name if the file is busy!
*/
if (daOwnerPtr == nil)
daOwnerPtr = PEntryFromFileSpec(pDASpec->daFileSpec, pDAName);
if ((alreadyLaunched = (daOwnerPtr != nil)) == false)
{
/* Try to launch the DA Handler (implicitly setting daOwnerPtr) */
StandardLaunch(nil, 0, modeDeskAccessory+FILE_FLAG_MULTILAUNCH, nil, &dahLaunchResults, pDASpec);
if ((retval = dahLaunchResults.LaunchError) != noErr)
goto ErrorRecovery;
/* Nuke dad if opening the DA was just a knee-jerk reaction by the
* application, since the relationship is accidental.
*/
if (fromOpenDeskAcc)
daOwnerPtr->p_dadProcess = nil;
}
/* If DA belongs to someone else: bring it forward. If it's brand new, make it sleep
* until it can come forward (avoids flash).
* NOTE: Assumes that DA is not using this call to bring itself forward (since we'll just
* _OpenDeskAcc). OK, tho. DA could use SetFrontProcess instead.
*/
if (daOwnerPtr != pCurrentProcess)
{
if ((SetFrontProcess(&daOwnerPtr->p_serialNumber) == noErr) && (alreadyLaunched == false))
PutOnSleepQueue(daOwnerPtr, MAXVALUE);
}
/* Owner is current process: load the DA!
* NOTE: Must set the p_daResource before call through, in case the call through
* results in a context switch (I've seen DAs put up StandardFile here!).
*/
else
{
if ((coercionState == CS_DONE) || (coercionState == CS_ENTERING))
{
short callThroughResult;
if (((pCurrentProcess->p_taskmode & modeDeskAccessory) != 0) && (daResourceNum != 0))
(*daOwnerPtr->p_pcb)->p_daResource = daResourceNum;
oldtrap = patchtraps[OPENDESKACC].oldtrap;
callThroughResult = CALL_FNC_PTR(Opendeskacc_ptr,oldtrap,((pDAName != nil) ? pDAName : &rsrcName));
if (pDrvrNum != nil)
*pDrvrNum = callThroughResult;
}
else
{
dbmsg("Bad time to call LaunchDeskAccessory/_OpenDeskAcc");
}
}
ErrorRecovery:
A5Restore(olda5);
return (retval);
#undef daOwnerPtr
}
/* c_opendeskacc. Our patch to OpenDeskAcc. This trap used to mean "open
* desk accessory". Now it means "I don't recognize the Apple menu item the
* user selected, what do you make of it?". What we make of it is one of
* about a half dozen things that we stick in the Apple menu: a phantom item to
* aid scrap coercion, a process name to switch to, a command, our about box,
* a speciality item added by an application, or (last but not least) a DA.
*/
pascal short
c_opendeskacc(StringPtr stringPtrDAName)
{
short retval;
unsigned long olda5;
AppleItemHdl hAppleItem;
olda5 = ProcessMgrA5Setup();
/* In the middle of scrap coercion? Handle this state from here. Install a fake
* DA window. Set coercionState so app does the proper activation (i.e. if there
* is already a window, expect a deactivate on it before an activate on ours).
*/
retval = FAKE_OPENDA_RETVAL;
if ((hAppleItem = StringToAppleItemHdl(stringPtrDAName)) != nil)
{
SendAppleMenuSelectMsg(hAppleItem);
}
/* Item is nothing we know as special. Try to open as a desk accessory. */
else
{
DASpecBlock daSpec;
daSpec.daFileSpec = nil;
daSpec.daDrvrName = stringPtrDAName;
if (CoreLaunchDeskAccessory(&daSpec, &retval, true) != noErr)
SysBeep(20);
}
A5Restore(olda5);
return retval;
}
/* c_LaunchDeskAccessory. Cause the specified DA to be launched and/or frontmost.
* If the file name is not specified, assume that DA is in the current chain. If
* the file name is specified, but the resource name is not, use the first DRVR
* resource in the file.
*/
pascal OSErr
c_LaunchDeskAccessory(FSSpec *pFileSpec, StringPtr pDAName)
{
DASpecBlock daSpec;
daSpec.daFileSpec = pFileSpec;
daSpec.daDrvrName = pDAName;
return(CoreLaunchDeskAccessory(&daSpec, nil, false));
}
/* c_sysedit. Patch to increment SCRAPCOUNT if the fake window is on top. */
pascal short
c_sysedit(short editCmd)
{
unsigned long olda5;
WindowPeek windowPeek;
pascal void (*oldtrap)();
short retval;
/* Locate top window */
windowPeek = WINDOWLIST;
/* Check the windowKind field to see if it's mine */
if ((windowPeek != nil)
&& (windowPeek->windowKind == SCRAP_COERCION_FAKE_DA_WINDOW_ID))
{
/* If cut or copy bump the scrapcount. Pretend we accepted it
* in any case.
*/
CheckForCutCopySysEditCmd(editCmd, &SCRAPCOUNT);
retval = wTrue;
}
else
{
olda5 = ProcessMgrA5Setup();
/* Check if we came in here for a cut or copy */
CheckForCutCopySysEditCmd(editCmd, &cutCopyCount);
oldtrap = patchtraps[SYSEDIT].oldtrap;
A5Restore(olda5);
retval = CALL_FNC_PTR(Sysedit_ptr,oldtrap,(editCmd));
}
return retval;
}
/* c_systemmenu. Our chance to have real menus! */
pascal void
c_systemmenu(long menuResult)
{
PEntryPtr pProc;
LayerPeek pNextLayer;
Boolean hideFront;
unsigned long olda5;
pascal void (*oldtrap)();
olda5 = ProcessMgrA5Setup();
oldtrap = patchtraps[SYSTEMMENU].oldtrap;
/* Was it our menu (versus a DA or other system menu)? */
if (MENU_ID_OF_SELECTION(menuResult) != kApplicationMenuID)
{
A5Restore(olda5);
CALL_FNC_PTR(Systemmenu_ptr,oldtrap,(menuResult));
return;
}
/* Our menu was selected from. Do the right thing. */
pProc = nil;
switch (ITEM_ID_OF_SELECTION(menuResult))
{
case HIDE_APP_ITEM:
hideFront = true;
if ((pNextLayer = LGetNextLayer(GetLayer(), true)) != nil)
pProc = LayerOwner(pNextLayer);
break;
case HIDE_OTHERS_ITEM:
case SHOW_ALL_ITEM:
ShowHideOthers(ITEM_ID_OF_SELECTION(menuResult) == SHOW_ALL_ITEM);
AdjustApplicationMenu(nil);
break;
/* Process name was selected */
default:
{
/* Sample option key promptly */
hideFront = OptionKeyIsDown();
/* Initiate foreground switch to the selected process */
if ((pProc = PEntryFromApplicationMenuItem(ITEM_ID_OF_SELECTION(menuResult))) == pFrontProcess)
pProc = nil;
break;
}
};
/* Handle foreground switch and optional hiding */
if ((pProc != nil) && (SetFrontProcess(&pProc->p_serialNumber) == noErr) && hideFront)
HideLayer(GetFrontAppLayer(), false);
/* Disemblazon the menu */
HiliteMenu(0);
A5Restore(olda5);
}
/* c_systemclick. Yet another step in scrap coercion. */
pascal void
c_systemclick(EventRecord *evtPtr, WindowPeek *windowPtr)
{
unsigned long olda5;
pascal void (*oldtrap)();
olda5 = ProcessMgrA5Setup();
/* Coercion state CS_CLICK_IN_WINDOW said mouse down in fake window, and
* sets state to CS_TAKE_DOWN_WINDOW. App responds by calling _SystemClick,
* so now take down fake window to simulate a _CloseDeskAcc. Next call(s)
* to _WaitNextEvent, _GetNextEvent, or _EventAvail should return the activate
* events for the (new) top window.
*/
if (coercionState == CS_TAKE_DOWN_WINDOW)
{
TakeDownFakeWindow();
}
else
{
oldtrap = patchtraps[SYSTEMCLICK].oldtrap;
CALL_FNC_PTR(Systemclick_ptr,oldtrap,(evtPtr,windowPtr));
}
A5Restore(olda5);
}
#define IS_UPDATE_OR_ACTIVATE_EVENT(eCode) ( (eCode == updateEvt) || (eCode == activateEvt) )
/* c_systemevent. If its a key or any event with our window in the message field,
* bump SCRAPCOUNT and return true.
* NOTE: Like much of the desk mgr, we don't understand completely why we can't
* can't call the old routine inside of _MenuSelect. There is no apparent reason
* why we should, except to pass on an update event that would invalidate the menu
* saved-behind bits (but this should never happen). Right now we always pass thru
* disk-insert events since we need to always get them mounted.
*/
pascal short
c_systemevent(EventRecord *theevent)
{
unsigned long olda5;
WindowPeek windowPeek;
LayerPtr pLayer;
short eventCode;
Boolean isCmdKeyDown;
short dontPassThru;
pascal void (*oldtrap)();
short retval;
eventCode = theevent->what;
isCmdKeyDown = ((eventCode == keyDown) && ((theevent->modifiers & cmdKey) != 0));
/* Get the window we're going to deal with, based on the event handling table in ROM _SystemEvent */
windowPeek = IS_UPDATE_OR_ACTIVATE_EVENT(eventCode) ? (WindowPeek)theevent->message : (WindowPeek) FrontWindow();
/* If it is our fake DA window, swallow the events, possibly looking for cut or copies */
if (windowPeek != nil && windowPeek->windowKind == SCRAP_COERCION_FAKE_DA_WINDOW_ID)
{
if (isCmdKeyDown)
CheckForCutCopyMenuKey((char)theevent->message, &SCRAPCOUNT);
/* Have to return false for pseudoevents */
return (theevent->what != app4Evt);
}
olda5 = ProcessMgrA5SimpleSetup();
oldtrap = patchtraps[SYSTEMEVENT].oldtrap;
dontPassThru = wFalse;
retval = wFalse;
if ( (coercionState == CS_DONE) && imLayerKeyDown( theevent, true) )
{
theevent->what = nullEvent;
dontPassThru = wTrue;
retval = wTrue;
}
dontGetFakeKey = 0;
if (isCmdKeyDown && coercionState == CS_DONE)
CheckForCutCopyMenuKey((char)theevent->message, &cutCopyCount);
/* Is the event an update to a window in another app's layer? If so, we need to
* return wTrue (so app doesn't try to handle event for window it doesn't own), AND
* not call through (preventing SystemEvent from trying to give the update to a DA
* that is currently switched out.
*/
if (eventCode == updateEvt)
{
if ( ((pLayer = FindAppLayer(windowPeek)) != nil) &&
(LayerOwner(pLayer) != pCurrentProcess) )
{
dontPassThru = wTrue;
retval = wTrue;
}
}
/* No? Check for the debugger key */
else if ( (pDebugProcess != nil)
&& (theevent->what == keyDown)
&& (*((char *) &theevent->message + 2) == debugControlKeyCode)
&& ((theevent->modifiers & (cmdKey | optionKey)) == (cmdKey | optionKey)) )
{
/* Saw the debug key from a "safe" level. Reset counter because we
* succeeded getting from PostEvent to GNE to SystemEvent. Make a
* system error that the debugger will interpret as "user hit the
* debugger key". This SysError will return!
*/
debugKeyTryCount = -1;
SysError(enterDebugger);
dontPassThru = wTrue;
}
A5SimpleRestore(olda5);
if ((dontPassThru == wFalse) || theevent->what == diskEvt)
retval = CALL_FNC_PTR(Systemevent_ptr,oldtrap,(theevent));
return retval;
}
#ifdef WRITENOW_FIX
/* ********** NOTE: The following 2 routines are done for WriteNow to work. ********* */
/* OverlaySystemEvent. Set a5 to CURRENTA5, then call app's _SystemEvent.
* NOTE: This is a hack for WriteNow that should be taken out when it is rev'ed
* (or at least check for 'VERS').
*/
pascal short
OverlaySystemEvent(EventRecord *theevent)
{
unsigned long olda5;
pascal void (*apptrap)();
short retval;
/* Get app's patch routine addr */
olda5 = ProcessMgrA5SimpleSetup();
(ProcPtr)apptrap = (*pCurrentProcess->p_pcb)->appSystemEventPatch;
A5SimpleRestore(olda5);
/* Call it using CURRENTA5, but SystemEvent caller's graphics pointer */
olda5 = CurrentA5Setup();
retval = CALL_FNC_PTR(Systemevent_ptr,apptrap,(theevent));
A5Restore(olda5);
return retval;
}
/* SetOverlaySystemEvent. Set up SystemEvent patch address in current process, and
* return the address of our overlay.
* NOTE: Assumes a5 == PROCESSMGRGLOBALS.
*/
ProcPtr
SetOverlaySystemEvent(ProcPtr apptrap)
{
(*pCurrentProcess->p_pcb)->appSystemEventPatch = apptrap;
return ((ProcPtr) OverlaySystemEvent);
}
/* **************************** */
#endif WRITENOW_FIX
/* LimitFrontSleepForDriversSake. Calculate a desired maximum sleep time based on
* how soon the next driver needs to be called (drivers are called via SystemTask which
* is called by old apps and by event_common).
* NOTE: Assumes that pCurrentProcess == pFrontProcess, since drivers installed in
* the UNITTABLE but local to the current (non-FG) process are irrelevant. Furthermore,
* this must be called *before* the process has actually started to sleep, since we don't
* monkey around with the sleep queue.
* NOTE: Best called after SystemTask, since SystemTask may satisfy current need and
* advance dCtlDelay.
*/
void
LimitFrontSleepForDriversSake(void)
{
register unsigned long nextCall, minSoFar;
register DCtlPtr dCtlPtr;
DCtlHandle *pDCtlHdl;
short loopCount;
/* Shortcut if process is not planning on sleeping anyway */
if (pCurrentProcess->p_wakeuptime == 0)
return;
/* DA Handler should not sleep in the foreground, since it has a driver that
* probably needs cursor calls.
*/
if ((pCurrentProcess->p_taskmode & modeDeskAccessory) != 0)
minSoFar = 0;
else
{
minSoFar = 0xFFFFFFFF;
pDCtlHdl = UNITTABLE;
loopCount = UNITNTRYCNT;
while ((--loopCount >= 0) && (minSoFar != 0))
{
if ( (*pDCtlHdl != nil) && ((dCtlPtr = **pDCtlHdl) != nil) && WantsTime(dCtlPtr) )
{
/* Check when driver should be called, handling dCtlDelay < MIN_DRIVER_DELAY
* and instances where we're late giving the driver time. Also checks
* for wraparound (in which case minSoFar should be min'd with 0xFFFFFFFF,
* a NOP we can avoid!).
*/
if ((nextCall = (unsigned long) dCtlPtr->dCtlCurTicks + dCtlPtr->dCtlDelay) >= (unsigned long) dCtlPtr->dCtlCurTicks)
{
if (nextCall <= TICKS)
nextCall = (dCtlPtr->dCtlDelay < MIN_DRIVER_DELAY) ? TICKS + MIN_DRIVER_DELAY : 0;
if (nextCall < minSoFar)
minSoFar = nextCall;
}
}
pDCtlHdl++;
}
}
/* Limit sleep time to calculated value. */
if (pCurrentProcess->p_wakeuptime > minSoFar)
pCurrentProcess->p_wakeuptime = minSoFar;
}
/* LimitBackSleepForDriversSake. Calculate a desired maximum sleep time based on
* how soon the next driver needs to be called (drivers are called via SystemTask which
* is called by old apps and by event_common).
* NOTE: This must be called *before* the process has actually started to sleep, since
* we don't monkey around with the sleep queue.
* NOTE: Best called after SystemTask, since SystemTask may satisfy current need and
* advance dCtlDelay.
*/
void
LimitBackSleepForDriversSake(void)
{
register DCtlPtr dCtlPtr;
register unsigned long nextCall, minSoFar;
DCEDescHdl ppDCEDesc;
DCEDescPtr pDCEDesc;
unsigned short cDCE;
/* Shortcut if process is not planning on sleeping anyway */
if (pCurrentProcess->p_wakeuptime == 0)
return;
/* Get handle to DCE switch table, and find out how many local drivers there are.
* Leave now if there are nonesuch.
*/
if ((cDCE = GetHandleSize((ppDCEDesc = (*(pCurrentProcess->p_pcb))->dces))) == 0)
return;
/* Loop through the DCE switch table. */
pDCEDesc = *ppDCEDesc;
cDCE /= sizeof(**ppDCEDesc);
minSoFar = 0xFFFFFFFF;
do
{
if ( ((dCtlPtr = *((DCtlHandle) UNITTABLE[pDCEDesc->d_unit])) == nil) || (WantsTime(dCtlPtr) == false) )
continue;
/* Check when driver should be called, handling dCtlDelay < MIN_DRIVER_DELAY
* and instances where we're late giving the driver time. Also checks
* for wraparound (in which case minSoFar should be min'd with 0xFFFFFFFF,
* a NOP we can avoid!).
*/
if ((nextCall = (unsigned long) dCtlPtr->dCtlCurTicks + dCtlPtr->dCtlDelay) >= (unsigned long) dCtlPtr->dCtlCurTicks)
{
if (nextCall <= TICKS)
nextCall = (dCtlPtr->dCtlDelay < MIN_DRIVER_DELAY) ? TICKS + MIN_DRIVER_DELAY : 0;
if (nextCall < minSoFar)
minSoFar = nextCall;
}
/* Advance to next descriptor (let the counter catch us going off the end) */
pDCEDesc += 1;
} while ((--cDCE > 0) && (minSoFar != 0));
/* Limit sleep time to calculated value. */
if (pCurrentProcess->p_wakeuptime > minSoFar)
pCurrentProcess->p_wakeuptime = minSoFar;
}
/* GiveLocalDriversTime. Give accRun calls to drivers local to the current process.
* NOTE: It only gives time to drivers that were switched out of the unit table
* when this guy was switched out, so if this guy opened one after the switch, he'll
* have to wait for the next go-around to get the guy some time.
* NOTE: Routine optimized for case where task has no local drivers.
*/
void
GiveLocalDriversTime(void)
{
register DCtlPtr dCtlPtr;
DCEDescHdl ppDCEDesc;
DCEDescPtr pDCEDesc;
unsigned short cDCE;
unsigned long tickDelay;
CntrlParam ctlPB;
/* Get handle to DCE switch table, and find out how many local drivers there are.
* Leave now if there are nonesuch.
*/
if ((cDCE = GetHandleSize((ppDCEDesc = (*(pCurrentProcess->p_pcb))->dces))) == 0)
return;
/* Lock ppDCEDesc, because a control call could conceivably cause movement in
* the Process Mgr heap, most likely by causing sys heap growth.
*/
HLock(ppDCEDesc);
/* Loop through the DCE switch table. Calling each driver noted therein.
* NOTE: dCtlPtr could come directly from *ppDCEDesc->d_handle, but then we lose
* the guarantee that it is actually in the unit table. A misbehaving app might
* pull the guy out of the unit table, but leave him intact between the time the
* app was switched in, and the time SystemTask was called.
*/
pDCEDesc = *ppDCEDesc;
cDCE /= sizeof(**ppDCEDesc);
do
{
if ( ((dCtlPtr = *((DCtlHandle) UNITTABLE[pDCEDesc->d_unit])) == nil) || (WantsTime(dCtlPtr) == false) )
continue;
tickDelay = dCtlPtr->dCtlDelay;
if (tickDelay == 0 || (unsigned long)dCtlPtr->dCtlCurTicks + tickDelay < TICKS)
{
/* Update him to show that he got time */
dCtlPtr->dCtlCurTicks = TICKS;
/* Give him some time */
ctlPB.ioCRefNum = dCtlPtr->dCtlRefNum;
ctlPB.csCode = accRun;
ControlImmed(&ctlPB);
/* Result can be positive since this is an immediate call */
assert(ctlPB.ioResult >= 0);
}
/* Advance to next descriptor (let the counter catch us going off the end) */
pDCEDesc += 1;
} while (--cDCE > 0);
HUnlock(ppDCEDesc);
}
/* c_systemtask. A patch for SystemTask. Do nothing unless the calling process and
* coercion are in a generic state, and the fake DA window is not up (can the window
* be even be up when coercionState == CS_DONE?). We can call the original routine
* only if the current app is the frontmost, since the cursor handling and notification
* manager rely on the WINDOWLIST to contain the active window. If the current app is
* not frontmost, we do the remaining part of the original routine. Currently, this
* only means giving control calls to the needs-time drivers.
* NOTE: We disallow switching so we don't get screwed up by DA's that make event calls
* (we could end up back here, and call them again, resulting in an infinite loop).
* NOTE: We bracket the call with BeginSystemMode/EndSystemMode so that files opened
* by permanent system drivers are not closed when an app quits. Unfortunately, this
* is not quite bombproof because some "permanent drivers" (like QuickMail) are really
* DAs that make event calls, which gives another app the chance to die. ExitToShell
* necessarily resets the SystemModeLevel to 0. A file opened later in the same
* control call would be tracked. This shouldn't screw up legitimate users of system
* mode, tho, because of the rule that it not be in effect over an event call.
* NOTE: Have to set up CURRENTA5 because of case where a DA might bring up an
* alert. The problem is that if the alert causes any kind of event for one
* of the underlying app's dialog windows, which might have a user item
* installed and the user item might expect A5 == CURRENTA5.
*/
pascal void
c_systemtask(void)
{
register PEntryPtr pCurPEntry;
pascal void (*oldtrap)();
unsigned long olda5;
/* Give the system/toolbox some time */
SynchIdleTime();
olda5 = ProcessMgrA5Setup();
pCurPEntry = pCurrentProcess;
/* In FG: do old trap if not in the middle of menu activity or switching */
if (pCurPEntry->p_state == PRRUN)
{
if ((coercionState == CS_DONE) &&
(WINDOWLIST == nil || WINDOWLIST->windowKind != SCRAP_COERCION_FAKE_DA_WINDOW_ID))
{
(void) BeginSystemMode();
oldtrap = patchtraps[SYSTEMTASK].oldtrap;
A5Restore(CURRENTA5);
CALL_FNC_PTR(Systemtask_ptr, oldtrap, ());
A5Restore(PROCESSMGRGLOBALS);
(void) EndSystemMode();
}
}
/* In BG: restrict activity to control calls for current drivers. */
else if (pCurPEntry->p_state == PRBACKRUN)
GiveLocalDriversTime();
A5Restore(olda5);
}