mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-11-29 05:49:19 +00:00
573 lines
14 KiB
C
573 lines
14 KiB
C
/*
|
|
File: Aux.c
|
|
|
|
Contains: Process Mgr support for A/UX.
|
|
NOTE: None of the code in this file is referenced unless the build
|
|
defines the HAS_AUX_PROCESSMGR condition.
|
|
|
|
Written by: David Harrison
|
|
|
|
Copyright: © 1980-1992 by Apple Computer, Inc., all rights reserved.
|
|
|
|
Change History (most recent first):
|
|
|
|
<5> 8/26/92 DTY Roll in latest changes for A/UX.
|
|
<4> 4/1/91 DFH (with John Iarocci) Bring this file up to date.
|
|
<2> 12/5/90 DFH Fixed warning in AUX_Block re: unused param.
|
|
<0> 12/4/90 DFH New Today.
|
|
|
|
*/
|
|
|
|
#include <Files.h>
|
|
#include <QuickDraw.h>
|
|
#include <Resources.h>
|
|
#include <Retrace.h>
|
|
#include <Errors.h>
|
|
#include <MFPrivate.h>
|
|
|
|
#include "Data.h"
|
|
#include "OSDispatchPrivate.h"
|
|
#include "Glue.h"
|
|
#include "SysMisc.h"
|
|
#include "Aux.h"
|
|
#include "Patches.h"
|
|
#include "Lomem.h"
|
|
|
|
/* Variables local to this module */
|
|
static short auxMaxSelector = -1;
|
|
static long ProcessMgrTaskID = -1;
|
|
static Getnextevent_ptr realGNE;
|
|
static Eventavail_ptr realEA;
|
|
static AUXDispatch_ptr realAD;
|
|
|
|
/* Some function prototypes that should be in (yet another) header file */
|
|
OSErr NewProcess(InternalLaunchPBPtr pParams);
|
|
short GetNextPseudoEvent(short, EventRecord *, Boolean);
|
|
void OpenAppWDs(PEntryPtr);
|
|
|
|
/* Handy macro to determine whether given event is best handled by AUX. */
|
|
#define IsAUXEvent(pEvent) ((pEvent->what >= FIRST_AUX_EVENT) && (pEvent->what <= LAST_AUX_EVENT))
|
|
|
|
#pragma segment AUX_SEGMENT
|
|
|
|
/* AUX_Setup. Set things up as necessary for A/UX. */
|
|
void
|
|
AUX_Setup(void)
|
|
{
|
|
/* Determine level of functionality. This check can probably go away once
|
|
* we settle on a common version of A/UX
|
|
*/
|
|
auxMaxSelector = AUX_Highest();
|
|
|
|
/* Set extra patches so we can fool ourselves */
|
|
realGNE = (Getnextevent_ptr) NGetTrapAddress(0xa970, ToolTrap);
|
|
NSetTrapAddress((long) AUX_GetNextEvent, 0xa970, ToolTrap);
|
|
|
|
realEA = (Eventavail_ptr) NGetTrapAddress(0xa971, ToolTrap);
|
|
NSetTrapAddress((long) AUX_EventAvail, 0xa971, ToolTrap);
|
|
|
|
realAD = (AUXDispatch_ptr) NGetTrapAddress(0xabf9, ToolTrap);
|
|
NSetTrapAddress((long) AUX_Dispatch_Patch, 0xabf9, ToolTrap);
|
|
}
|
|
|
|
/* AUX_EventAvail. Similar to our replacement for getnextevent, we also replace
|
|
* EventAvail for usage by Process Mgr only.
|
|
*/
|
|
pascal short
|
|
AUX_EventAvail(short evtMask, EventRecord *theEvent)
|
|
{
|
|
Boolean oldMask;
|
|
EventRecord anEvent;
|
|
|
|
oldMask = AUX_SetAUXMask(true);
|
|
for (;;)
|
|
{
|
|
(void) (*realEA)(evtMask, theEvent);
|
|
|
|
if (IsAUXEvent(theEvent) == false)
|
|
break;
|
|
|
|
/* if an A/UX event is available, remove it from the queue and deal with it */
|
|
AUX_GetAnyEventGlue(0, &anEvent, 0, 0, true);
|
|
(void) AUX_HandleEvent(&anEvent);
|
|
};
|
|
|
|
(void) AUX_SetAUXMask(oldMask);
|
|
|
|
return theEvent->what == nullEvent ? wFalse : wTrue;
|
|
}
|
|
|
|
/* AUX_Dispatch_Patch. Patch AUX_Dispatch so we can look for a AUX_BEGINAPPLICATION
|
|
* selector and execute within the ProcessManagers context
|
|
*/
|
|
pascal long
|
|
AUX_Dispatch_Patch(short selector, char *p)
|
|
{
|
|
unsigned long olda5;
|
|
long retval;
|
|
|
|
olda5 = ProcessMgrA5SimpleSetup();
|
|
|
|
if (selector != AUX_BEGINAPPLICATION)
|
|
retval = (*realAD)(selector, p);
|
|
else
|
|
/* Fix up working directories for home directory and files in APPPARMHANDLE */
|
|
OpenAppWDs(pCurrentProcess);
|
|
|
|
A5SimpleRestore(olda5);
|
|
|
|
return(retval);
|
|
}
|
|
|
|
/* AUX_Block. */
|
|
void
|
|
AUX_Block(PEntryPtr)
|
|
{
|
|
}
|
|
|
|
/* AUX_CoffLaunch. If a coff binary toolbox program is executed, it will first issue a
|
|
* UI_ATTACH ioctl, which will send us an attachEvt. Upon seeing the attach event, we
|
|
* use AUX_MakeLayer to create a minimal process which can then be switched to using the
|
|
* normal context switching code. The coff binary will then call (via OSDispatch) this
|
|
* routine to really fill the process out. All this is necessary because the coff
|
|
* program must execute in a separate A/UX process and it's accesses to the shared data
|
|
* areas must be synchronized.
|
|
*/
|
|
pascal short
|
|
AUX_CoffLaunch(u_long reqSize, u_long taskmode, StringPtr procName, short vrefnum, u_long ssize)
|
|
{
|
|
#pragma unused (reqSize, taskmode, procName, vrefnum, ssize)
|
|
|
|
return pCurrentProcess->p_mypid;
|
|
}
|
|
|
|
/* AUX_FindProc. Find the process associated with taskId. */
|
|
PEntryPtr
|
|
AUX_FindProc(taskId)
|
|
{
|
|
PEntryPtr pProc;
|
|
|
|
pProc = pProcessList;
|
|
while (pProc != nil)
|
|
{
|
|
if (pProc->aux_realpid == taskId)
|
|
break;
|
|
|
|
pProc = pProc->p_NextProcess;
|
|
}
|
|
|
|
return pProc;
|
|
}
|
|
|
|
/* AUX_ForkExec. Fork and exec a new coff binary. */
|
|
short
|
|
AUX_ForkExecGlue(short vrefnum, char *name)
|
|
{
|
|
ForkExecRec params;
|
|
|
|
assert(AUX_FORKEXEC <= auxMaxSelector);
|
|
|
|
params.vrefnum = vrefnum;
|
|
params.name = name;
|
|
if (AUX_ForkExec(¶ms) != 0)
|
|
return permErr;
|
|
else
|
|
return params.pid;
|
|
}
|
|
|
|
/* AUX_GetAnyEventGlue. Handy routine for calling AUX_GetAnyEvent, since latter is
|
|
* rather awkward.
|
|
*/
|
|
short
|
|
AUX_GetAnyEventGlue(short evtMask, EventRecord *theEvent, long timeout, RgnHandle bounds, Boolean pullit)
|
|
{
|
|
GetAnyEventRec params;
|
|
|
|
assert(AUX_GETANYEVENT <= auxMaxSelector);
|
|
|
|
params.mask = evtMask;
|
|
params.timeout = timeout;
|
|
params.mouseBounds = bounds;
|
|
params.pullIt = pullit;
|
|
|
|
(void) AUX_GetAnyEvent(¶ms);
|
|
|
|
*theEvent = params.event;
|
|
|
|
return theEvent->what != nullEvent ? -1 : 0;
|
|
}
|
|
|
|
/* AUX_GetMinTimeout. Compute the minimum timeout necessary, we use this so we can
|
|
* sleep in the kernel instead of spinning in user mode.
|
|
*/
|
|
long
|
|
AUX_GetMinTimeout(void)
|
|
{
|
|
extern Boolean napOver;
|
|
|
|
if ((pFrontProcess->p_waitnexteventlevel > 0) /* GetNextEvent never blocks */
|
|
&& (coercionState == CS_DONE) /* if coercing, don't want physical events */
|
|
&& (napOver == 0) /* somebody needs attention */
|
|
&& (pSleepingStateList != nil)) /* somebody is sleeping */
|
|
return (pSleepingStateList->p_wakeuptime - TICKS);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* AUX_GetMouseBounds. Compute the rectangle for mouse moved events. Once again, this
|
|
* is so we can sleep in the kernel instead of user mode spinning.
|
|
*/
|
|
RgnHandle
|
|
AUX_GetMouseBounds(void)
|
|
{
|
|
extern RgnHandle mousebox;
|
|
|
|
return mousebox;
|
|
}
|
|
|
|
/* AUX_GetNextEvent. Our replacement for the real GetNextEvent. This isn't the one
|
|
* seen by the user program, but is the one we will use to actually get events out of
|
|
* the queue. Ours is different because we can get some funky events back from the
|
|
* kernel and mere mortals shouldn't ever get them.
|
|
*
|
|
* NOTE: ProcessManager overpatches this trap, but calls us in event_common when
|
|
* the process state is PRRUN.
|
|
*/
|
|
pascal short
|
|
AUX_GetNextEvent(short evtMask, register EventRecord *theEvent)
|
|
{
|
|
Boolean oldMask;
|
|
short realRetVal;
|
|
|
|
oldMask = AUX_SetAUXMask(true);
|
|
|
|
while ((realRetVal = (*realGNE)(evtMask, theEvent)) && AUX_HandleEvent(theEvent))
|
|
{
|
|
/* Just loop. All the work is done in the loop control. */
|
|
}
|
|
|
|
(void) AUX_SetAUXMask(oldMask);
|
|
|
|
return realRetVal;
|
|
}
|
|
|
|
/* AUX_GetTaskGlue. Handy utility to return the A/UX style task identifier of this
|
|
* process so we can do a real context switch to it later.
|
|
*/
|
|
long
|
|
AUX_GetTaskGlue(void)
|
|
{
|
|
long taskId;
|
|
|
|
assert(AUX_GETTASK <= auxMaxSelector);
|
|
AUX_GetTask(&taskId);
|
|
return taskId;
|
|
}
|
|
|
|
/* AUX_HandleEvent. Handle an A/UX specific event. */
|
|
short
|
|
AUX_HandleEvent(EventRecord *theEvent)
|
|
{
|
|
short retVal;
|
|
|
|
retVal = wTrue;
|
|
if (IsAUXEvent(theEvent) == false)
|
|
return wFalse;
|
|
|
|
switch(theEvent->what)
|
|
{
|
|
/* somebody's UI_SETSELECT broke, make 'em runnable. */
|
|
case auxSelectEvent:
|
|
AUX_MakeRunnable(theEvent->message);
|
|
break;
|
|
|
|
/* a new layer was asynchronously created, fit it in to the scheduling */
|
|
case auxAttachEvent:
|
|
AUX_MakeLayer(theEvent->message, theEvent->modifiers, theEvent->when);
|
|
break;
|
|
|
|
/* a layer went away by mistake (kill(9...) for instance, clean it up */
|
|
case auxExitEvent:
|
|
AUX_KillLayer(theEvent->message);
|
|
break;
|
|
}
|
|
theEvent->what = nullEvent;
|
|
|
|
return retVal;
|
|
}
|
|
|
|
#define LAUNCH_DELAY (60*60) /* one minute */
|
|
|
|
/* AUX_IdleProc. */
|
|
void
|
|
AUX_IdleProc(void)
|
|
{
|
|
register PEntryPtr myProc;
|
|
unsigned long olda5;
|
|
EventRecord anEvent;
|
|
|
|
olda5 = ProcessMgrA5Setup();
|
|
myProc = pCurrentProcess;
|
|
for (;;)
|
|
{
|
|
assert(myProc->aux_waitproc);
|
|
|
|
(void) AUX_GetAnyEventGlue(0, &anEvent, LAUNCH_DELAY, 0, true);
|
|
|
|
/* Handle the attach. aux_restore_ctx should get us out of here, never to return. */
|
|
if ((anEvent.what == auxAttachEvent) && (anEvent.message == myProc->aux_waitproc))
|
|
{
|
|
myProc->aux_realpid = myProc->aux_waitproc;
|
|
myProc->aux_waitproc = 0;
|
|
AUX_SwitchGlue(myProc->aux_realpid);
|
|
aux_restore_ctx(pCurrentProcess->p_sp);
|
|
dbmsg("How the ≈ did I get here?");
|
|
}
|
|
|
|
/* AUX event was not the usual attach, or an attach came from an incorrect
|
|
* AUX process. Feebly try to handle the event. Then die.
|
|
*/
|
|
(void) AUX_HandleEvent(&anEvent);
|
|
AUX_Kill(myProc->aux_waitproc);
|
|
ExitToShell();
|
|
}
|
|
}
|
|
|
|
/* AUX_KernelEvents. */
|
|
short
|
|
AUX_KernelEvents(EventRecord *theEvent)
|
|
{
|
|
short foundSome;
|
|
|
|
foundSome = 0;
|
|
while (AUX_GetAnyEventGlue(0, theEvent, 0, 0, 1) && IsAUXEvent(theEvent) != 0)
|
|
{
|
|
(void) AUX_HandleEvent(theEvent);
|
|
foundSome++;
|
|
}
|
|
|
|
return foundSome;
|
|
}
|
|
|
|
/* AUX_KillLayer. Get rid of an abnormally terminated process */
|
|
void
|
|
AUX_KillLayer(long taskId)
|
|
{
|
|
register PEntryPtr pProc;
|
|
|
|
if ((pProc = AUX_FindProc(taskId)) != nil)
|
|
(void) KillProcess(&pProc->p_serialNumber);
|
|
}
|
|
|
|
/* AUX_Launch. */
|
|
OSErr
|
|
AUX_Launch(InternalLaunchPBPtr pParams)
|
|
{
|
|
short realPid;
|
|
PEntryPtr pProc;
|
|
OSErr err;
|
|
WDPBRec wd;
|
|
|
|
/* setup a WDPBRec to pass to AUX_ForkExec through the AUXDispatch */
|
|
wd.ioCompletion = nil;
|
|
wd.ioNamePtr = nil;
|
|
wd.ioWDProcID = 0;
|
|
wd.ioVRefNum = pParams->ilAppSpecPtr->vRefNum;
|
|
wd.ioWDDirID = pParams->ilAppSpecPtr->parID;
|
|
PBOpenWD(&wd, true);
|
|
|
|
realPid = AUX_ForkExecGlue(wd.ioVRefNum, pParams->ilAppSpecPtr->name);
|
|
|
|
/* Close the WD, if we opened it */
|
|
if (wd.ioWDCreated != 0)
|
|
PBCloseWD(&wd, SyncHFS);
|
|
|
|
/* Negative result is an OSErr. Result should never be 0. Positive result is AUX pid. */
|
|
if (realPid < 0)
|
|
return((OSErr) realPid);
|
|
|
|
pParams->ilTaskMode |= COFF_BINARY;
|
|
if ((err = NewProcess(pParams)) != noErr)
|
|
{
|
|
dbmsg("AUX_Launch: NewProcess failed");
|
|
AUX_Kill(realPid);
|
|
return(err);
|
|
}
|
|
|
|
pProc = pParams->ilResultProc;
|
|
pProc->aux_waitproc = realPid;
|
|
pProc->aux_realpid = AUX_ProcessMgrTask();
|
|
|
|
return(noErr);
|
|
}
|
|
|
|
/* AUX_MakeLayer. Create an empty process. After the A/UX process switch it will be
|
|
* filled out by whomever did the UI_ATTACH.
|
|
*/
|
|
void
|
|
AUX_MakeLayer(long taskId, unsigned short flags, long size)
|
|
{
|
|
register PEntryPtr pProc;
|
|
InternalLaunchPB launchPB;
|
|
CoffName coffname;
|
|
|
|
coffname.taskid = taskId;
|
|
if ((pProc = AUX_Unblock(taskId)) != nil)
|
|
return;
|
|
|
|
if (AUX_COFF_FSSpec(&coffname) != noErr)
|
|
return;
|
|
|
|
launchPB.ilAppSpecPtr = &coffname.fsspec;
|
|
launchPB.ilTaskMode = COFF_BINARY | ASYNC_LAUNCH | (unsigned long) flags;
|
|
launchPB.ilDAInfoPtr = nil;
|
|
launchPB.ilPartitionSize = size + DEFLTSTACK - MACPLUS_DEFLTSTACK;
|
|
launchPB.ilStackSize = DEFLTSTACK;
|
|
launchPB.ilAppParameters = nil;
|
|
launchPB.ilAppParamHdl = nil;
|
|
if (NewProcess(&launchPB) != noErr)
|
|
{
|
|
dbmsg("AUX_MakeLayer: NewProcess failed");
|
|
AUX_Kill(taskId);
|
|
return;
|
|
}
|
|
|
|
pProc = launchPB.ilResultProc;
|
|
pProc->aux_realpid = taskId;
|
|
pProc->aux_waitproc = 0;
|
|
|
|
(void) WakeUpProcess(&pFrontProcess->p_serialNumber);
|
|
}
|
|
|
|
/* AUX_MakeRunnable. Make an application contained in a different task runnable. taskId
|
|
* refers to the kernel event layer identifier we got back from AUX_GetTask and stowed
|
|
* away in auxInfo.
|
|
*/
|
|
void
|
|
AUX_MakeRunnable(long taskId)
|
|
{
|
|
PEntryPtr pProc;
|
|
|
|
if ((pProc = AUX_FindProc(taskId)) != nil)
|
|
(void) WakeUpProcess(&pProc->p_serialNumber);
|
|
}
|
|
|
|
/* AUX_ProcessMgrTask. Return the task descriptor for Macintosh Applications */
|
|
long
|
|
AUX_ProcessMgrTask(void)
|
|
{
|
|
if (ProcessMgrTaskID < 0)
|
|
AUX_GetTask(&ProcessMgrTaskID);
|
|
|
|
return ProcessMgrTaskID;
|
|
}
|
|
|
|
/* AUX_SwitchGlue. Force an A/UX kernel context switch. */
|
|
void
|
|
AUX_SwitchGlue(long taskId)
|
|
{
|
|
long rval;
|
|
EventRecord anEvent;
|
|
|
|
assert(AUX_SWITCH <= auxMaxSelector);
|
|
|
|
/* If the Process Mgr process this task is associated with is gone, kill this task
|
|
* after the switch, if this is the original Process Mgr process we have to
|
|
* leave it laying around anyway.
|
|
* NOTE: Inverting taskID tells AUX_Switch to kill the originating process after
|
|
* enabling the new one.
|
|
*/
|
|
if (((rval = AUX_GetTaskGlue()) != AUX_ProcessMgrTask()) && AUX_FindProc(rval) == 0)
|
|
taskId = (-(taskId));
|
|
|
|
for (;;)
|
|
{
|
|
/* Try switch. On error we try to destroy the layer (AUX_KillLayer won't
|
|
* return). If it does, we aren't in a valid process, so we pick a new one.
|
|
*/
|
|
if (AUX_Switch(taskId) != 0)
|
|
{
|
|
AUX_KillLayer(taskId);
|
|
cpu_resched();
|
|
}
|
|
|
|
/* Normally, when we get back from the AUX_Switch, we will have already been
|
|
* made the current process. If the task id of this a/ux process is not the
|
|
* same as where we think we ought to be, we got woken up because the current
|
|
* process died (most likely, perhaps theres something else, so we're pretending
|
|
* to be paranoid) so we reap and handle any a/ux events. If it's true that
|
|
* the current process really did die, we will eventually receive an exitEvent,
|
|
* which will result in the associated Process Mgr process being killed and the
|
|
* processor reschedule, in which case we won't ever return to this routine.
|
|
*/
|
|
if (pCurrentProcess->aux_realpid == AUX_GetTaskGlue())
|
|
break;
|
|
|
|
/* AUX_HandleEvent won't return if the process we're switching to has exited,
|
|
* thus giving us an exitEvent
|
|
*/
|
|
do
|
|
{
|
|
(void) AUX_GetAnyEventGlue(0, &anEvent, 0l, 0l, true);
|
|
} while (AUX_HandleEvent(&anEvent));
|
|
|
|
dbmsg("AUX_SwitchGlue: probably shouldn't ever get here");
|
|
|
|
/* having handled whatever broke us out of our interminable sleep, try again */
|
|
}
|
|
}
|
|
|
|
/* AUX_Unblock. Find the nacent Process Mgr process associated with the given taskID,
|
|
* and declare it alive and well.
|
|
*/
|
|
PEntryPtr
|
|
AUX_Unblock(long taskId)
|
|
{
|
|
PEntryPtr pProc;
|
|
|
|
pProc = pProcessList;
|
|
while (pProc != nil)
|
|
{
|
|
if (pProc->aux_waitproc == taskId)
|
|
{
|
|
pProc->aux_waitproc = 0;
|
|
pProc->aux_realpid = taskId;
|
|
break;
|
|
}
|
|
|
|
pProc = pProc->p_NextProcess;
|
|
}
|
|
|
|
return pProc;
|
|
}
|
|
|
|
/* AUX_WaitForEvent. */
|
|
short
|
|
AUX_WaitForEvent(short mask, EventRecord *theEvent)
|
|
{
|
|
if (pCurrentProcess)
|
|
if (GetNextPseudoEvent(mask, theEvent, false))
|
|
return wTrue;
|
|
|
|
(void) AUX_GetAnyEventGlue(mask, theEvent, AUX_GetMinTimeout(), AUX_GetMouseBounds(), false);
|
|
|
|
return theEvent->what == nullEvent ? wFalse : wTrue;
|
|
}
|
|
|
|
/* AUX_WaitNextEvent. */
|
|
short
|
|
AUX_WaitNextEvent(short mask, EventRecord *theEvent, long timeout, RgnHandle mbox)
|
|
{
|
|
short rval;
|
|
|
|
AUX_SetTimeOut(timeout);
|
|
AUX_SetBounds(mbox);
|
|
|
|
rval = GetNextEvent(mask, theEvent);
|
|
|
|
AUX_SetTimeOut(0);
|
|
AUX_SetBounds(nil);
|
|
|
|
return rval;
|
|
}
|