sys7.1-doc-wip/ProcessMgr/DAHandler.c

1256 lines
34 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Hacks to match MacOS (most recent first):
<Sys7.1> 8/3/92 Reverted <16> by using the non-standard "MyGestalt" glue for an init-
time Gestalt call, and for freeing the Init code segment.
9/2/94 SuperMario ROM source dump (header preserved below)
*/
/*
File: DAHandler.c
Contains: The System Desk Accessory Handler application
Written by: Phil Goldman
Copyright: © 1986-1993 by Apple Computer, Inc., all rights reserved.
Change History (most recent first):
<SM18> 7/21/93 joe Back out <SM17>.
<16> 9/25/92 DRF Get rid of MyGestalt
<15> 4/10/92 JSM Undo revision 14, we fixed the problem someplace else.
<14> 4/4/92 DTY #1019369,<FM>: For each desk accessory, remember the current
keyboard state, and the keyboard state after the driver has been
opened. On a resume, set the keyboard to the state the driver
left it, and on a suspend, set it to the original state.
<13> 3/23/92 JSM OSEvents.h is obsolete, use Events.h.
<12> 1/9/92 DTY In CleanupAndExit, check to see if there are any more desk
accessories running. If there arent, unload the DAHandler
segment. ThereAreMoreDeskAccessories ripped off from scotts 7
Up code. Also unload Init and %A5Init in main() after DAHandler
is done with them.
<11> 11/1/91 DTY TOPMAPHANDLE should be set to the top system override map in
SetupMenus so DAs can enjoy overridden system resources too.
Also, use the generic desk accessory icon if the desk accessory
is in a system override map.
<10> 4/4/91 DFH VL, WS#DFH-910404a : Fixed CloseDACleanupAndExit to be more
careful about a non-existent driver table entry.
<9> 1/10/91 DFH (dba) Got rid of InForeground, since it was just left over from
the sleep time calculation that got removed.
<7> 1/8/91 DFH (dba) Removed stack shrinkage from StartupCode() since stack
requirement is greater now that modal dialogs can call menu
select.
<6> 1/3/91 fjs (DFH) handle edit function keys
<5> 1/3/91 DFH (fjs) OpenTheDeskAccessory() was setting the myDriverRefnum
global to the OpenDeskAcc result. This is bad in the case that
the result was not a real refnum, since our cleanup/exit code
relies on it being -1 or a refnum. This resulted in wiping out
random memory as we tried to MemClear() the DCE in
CloseDACleanupAndExit().
<4> 12/10/90 DFH Added custom icon support.
<3> 12/5/90 DFH Remove sleep time calculation. Scheduler will do that for us
now.
<2> 11/19/90 DFH Robustified ExitToShell patch. Was same as Quit command, which
is not good in crash cases. Now sets myUnitNumber and
myDriverRefNum as early as possible, so we can deal with
ExitToShell in OpenDeskAcc (need to close driver if it got
open).
<0> x/xx/86 PYG New Today.
*/
#include <types.h>
#include <memory.h>
#include <resources.h>
#include <quickdraw.h>
#include <fonts.h>
#include <events.h>
#include <windows.h>
#include <dialogs.h>
#include <controls.h>
#include <menus.h>
#include <textedit.h>
#include <devices.h>
#include <desk.h>
#include <toolutils.h>
#include <errors.h>
#include <osutils.h>
#include <segload.h>
#include <diskinit.h>
#include <sound.h>
#include <GestaltEqu.h>
#include <Palettes.h>
#include <Icons.h>
#include <IconUtilsPriv.h>
#include <traps.h>
#include <Processes.h>
#include <Layers.h>
#include <ResourceMgrPriv.h>
#include "Lomem.h"
#include "SysMisc.h"
#include "Glue.h"
#undef assert
#ifdef DEBUG
#define assert(exp) {if (!(exp)) SysError(dsCoreErr);}
#else
#define assert(exp)
#endif DEBUG
/*
* Menu ID constants. These are the positive counterparts of the resource IDs.
*/
#define appleID 16473
#define fileID 16472
#define editID 16471
/*
* Menu item constants.
*/
#define appleMenu 0
#define aboutMeCommand 1
#define fileMenu 1
#define closeCommand 1
#define quitCommand 2
#define editMenu 2
#define undoCommand 1
#define cutCommand 3
#define copyCommand 4
#define pasteCommand 5
#define clearCommand 6
#define menuCount 3
/*
* For the About… DLOG
*/
#define aboutMeDLOG (-16473)
#define okButton 1
#define daIcon 2
#define daNameItem 3
#define firstAboutString 4
#define secondAboutString 5
#define AlertID_OutOfMem (-16472)
#define DitlID_OutOfMem (-16472)
#define AlertID_NotFound (-16471)
#define DitlID_NotFound (-16471)
#define StrID_InSysFile (-16471)
/* Extra slop to get up alert: Window, icon, etc. */
#define ALERT_SLOP_SIZE 0x800
/* Masks and a macro to ensure alignment of counts and addresses. Word alignment is
* required by the 68000 for non-byte pointer refereneces. Note that the result of
* WORD_ALIGN is the same as the original if the original was already word-aligned.
* Long alignment is for 68020 (and up) efficiency in general, and is a requirement
* of blocks in a 32-bit mode heap.
*/
#define LONGALIGN_MASK 0xFFFFFFFC
#define WORDALIGN_MASK 0xFFFFFFFE
#define LONG_ALIGN(value) (value = ((value + sizeof(long) - 1) & LONGALIGN_MASK))
#define WORD_ALIGN(value) (value = ((value + sizeof(short) - 1) & WORDALIGN_MASK))
#define LONG_ALIGNED(value) (((value) & (sizeof(long) - 1)) == 0)
#define WORD_ALIGNED(value) (((value) & (sizeof(short) - 1)) == 0)
/* HIWORD and LOWORD macros, for readability. */
#define HIWORD(aLong) (((aLong) >> 16) & 0xFFFF)
#define LOWORD(aLong) ((short)(aLong))
/* Macro to look up a particular byte in a pascal string */
#define StringByte(pStr, byteIndex) (*((char *) pStr + byteIndex + 1))
/* Macros for Resource Mgr peeking */
#define isNotDARefNum(refNum) (((refNum) >= 0) || ((refNum) <= -UNITNTRYCNT))
#define MapRefNumOffset 20
#define TopResFile() (*((short *) (*TOPMAPHANDLE + MapRefNumOffset)))
#define IsSystemRefNum(refNum) ((refNum == SYSMAP) || (refNum == 0))
/* Bit assignments in the dCtlFlags of a driver control block */
#define DNeedLock (1<<(8+6)) /* Driver will be locked when opened? */
#define DNeedTime (1<<(8+5)) /* Does driver need periodic control calls? */
#define DNeedGoodbye (1<<(8+4)) /* Does driver need a goodbye kiss? */
#define DStatEnable (1<<(8+3)) /* Can driver respond to Status calls? */
#define DCtlEnable (1<<(8+2)) /* Can driver respond to Control calls? */
#define DWritEnable (1<<(8+1)) /* Can driver respond to Write calls? */
#define DReadEnable (1<<(8+0)) /* Can driver respond to Read calls? */
#define DActive (1<<7) /* Is driver currently executing? */
#define DRamBased (1<<6) /* Is driver RAM-based? */
#define DOpened (1<<5) /* Is driver already open? */
/* Symbols for values of the asych parameter of HFS calls */
#define AsyncHFS true
#define SyncHFS false
/* Function prototypes */
void main(void);
void StartupCode(void);
void SetupMenus(void);
void DoCommand(long);
void OpenTheDeskAccessory(void);
OSErr PreflightDA(void);
void HandleCloseDA(short);
void CloseDACleanupAndExit(Boolean);
void ExitToShellPatch(void);
void MemClear(Ptr, unsigned long);
void PutUpDeathAlert(OSErr);
Boolean IsDrvrOpen(void);
/* Functions imported from DAHandler.a */
void CloseFrontDeskWindow(void);
void ClosePatch(void);
void SystemEventPatch(void);
StringPtr GetSysHeapString(void);
Handle GetMapHdlFromRefNum(short, Handle);
void InitAboutMeDialog(void);
/* From %A5Init */
extern void _DataInit();
/*
* Global Data objects.
*/
MenuHandle MyMenus[menuCount]; /* The menu handles */
Ptr oldCloseAddress; /* Address of old _Close routine */
Ptr oldSystemEventAddress; /* Address of old _SystemEvent routine */
Ptr oldExitToShellAddress; /* Address of old _ExitToShell routine */
short doneFlag; /* Time to quit yet? */
Boolean Colorized; /* Whether this machine has color capability */
Boolean Has32BitQD; /* Whether this machine has 32-bit QuickDraw */
short myFileRefNum; /* Refnum of file holding the DRVR */
short myDriverRefNum; /* Refnum of driver resource */
short myUnitNumber; /* the UNITTABLE index of my DA */
short myDisplayIconID; /* Resource ID of icon and info strings */
short wdShrinkOfAboutMe, htOfAboutMeStrings, htOfFirstAboutString, htOfSecondAboutString;
StringHandle hFirstAboutMeString, hSecondAboutMeString;
#define SetPSN(X, dst) {(dst)->highLongOfPSN = 0; (dst)->lowLongOfPSN = (X);}
/* ThereAreMoreDeskAccessories. Returns true if there are other desk accessories running. */
Boolean
ThereAreMoreDeskAccessories()
{
int count = 0;
ProcessSerialNumber psn = { 0, kNoProcess };
ProcessInfoRec info;
info.processInfoLength = sizeof(ProcessInfoRec);
info.processName = nil;
info.processAppSpec = nil;
while (GetNextProcess(&psn) == noErr)
{
if (GetProcessInformation(&psn, &info) == noErr)
{
if (info.processMode & modeDeskAccessory)
{
++count;
if (1 < count)
break;
}
}
}
return (1 < count);
}
/* CleanupAndExit. Remove any trace of our existence. */
void
CleanupAndExit(void)
{
/* Restore zone (prudent) and remove our patches */
THEZONE = APPLZONE;
NSetTrapAddress((long)oldCloseAddress, (short) _Close, OSTrap);
NSetTrapAddress((long)oldSystemEventAddress, (short) _SystemEvent, ToolTrap);
NSetTrapAddress((long)oldExitToShellAddress, (short) _ExitToShell, ToolTrap);
// <12> If there are no other desk accessories running, unload DAHandler.
if (!ThereAreMoreDeskAccessories())
UnloadSeg((Ptr) main);
/* Tell OS to destroy us altogether */
ExitToShell();
}
/* GiveGoodByeKiss. Give this driver a goodbye kiss if it wants one. */
GiveGoodByeKiss(DCtlPtr dCtlPtr)
{
CntrlParam goodByeParamBlock;
/* Give him a goodbye kiss, if necessary */
if ((dCtlPtr->dCtlFlags & DNeedGoodbye) != 0)
{
goodByeParamBlock.csCode = csCodeGoodBye;
goodByeParamBlock.ioCRefNum = dCtlPtr->dCtlRefNum;
PBControl(&goodByeParamBlock, SyncHFS);
}
}
/* MemClear. Two-bit routine to clear 'bytes' bytes starting at *'ptr' */
void
MemClear(register Ptr ptr, register unsigned long bytes)
{
do
{
*ptr++ = (char) 0x00;
}
while (--bytes != 0);
}
/* ExitToShellPatch. Our patch to ExitToShell. Give the driver a fighting chance at
* cleaning up
* NOTE: The etsDepth counter is to keep us from additional flailing if we are
* forced to ExitToShell even as we are closing the DA.
*/
void
ExitToShellPatch(void)
{
static short etsDepth = 0;
/* Try to do it nice. Then just do it. */
A5Restore(CURRENTA5);
if (etsDepth++ == 0)
CloseDACleanupAndExit(false);
else
CleanupAndExit();
}
/* CloseDACleanupAndExit. Patch for _ExitToShell. Gives the DA a chance to
* cancel (i.e. by leaving windows up).
*/
void
CloseDACleanupAndExit(Boolean beNice)
{
register DCtlPtr dCtlPtr;
register DCtlHandle dCtlHdl;
A5Restore(CURRENTA5);
if (myDriverRefNum != 0)
CloseDeskAcc(myDriverRefNum);
/* Quit if the DA did */
if ((beNice == false) || (FrontWindow() == nil))
{
if (myUnitNumber != 0)
{
dCtlHdl = ((DCtlHandle) UNITTABLE[myUnitNumber]);
if ((dCtlHdl != nil) && (dCtlPtr = *dCtlHdl) != nil)
{
if ((dCtlPtr->dCtlFlags & (DNeedGoodbye | DOpened)) == (DNeedGoodbye | DOpened))
GiveGoodByeKiss(dCtlPtr);
MemClear(dCtlPtr, sizeof(DCtlEntry));
dCtlPtr->dCtlRefNum = myDriverRefNum;
}
}
CleanupAndExit();
}
}
typedef struct MP
{
struct MP *pNextMP;
} MP;
/* Determine whether the current zone has at least the given number
* of free master pointers.
*/
Boolean
ThisManyMPs(unsigned short cMinAcceptableMPs)
{
MP *pCurMP;
for (pCurMP = (MP *)THEZONE->hFstFree; pCurMP != nil; pCurMP = pCurMP->pNextMP)
if (--cMinAcceptableMPs == 0)
return true;
return false;
}
#define MIN_ACCEPTABLE_MPS 32
#define kFunctionKey 0x10
#define kUndo 0x7A
#define kCut 0x78
#define kCopy 0x63
#define kPaste 0x76
void
main(void)
{
EventRecord myEvent;
WindowPtr whichWindow;
static Point bmPt = {100,100}; /* ULH pt of Disk Err dialog */
#define MAXSLEEPTIME (0xFFFFFFFF) /* largest value of WaitNextEvent sleep */
StartupCode();
/* Force all into system heap */
THEZONE = SYSZONE;
/* Because we are about to use this heap heavily */
if (ThisManyMPs(MIN_ACCEPTABLE_MPS) == false)
MoreMasters();
/* Get the sucker open */
OpenTheDeskAccessory();
// <12> Now that DAHandler is up and running, its Init and %A5Init segments can be
// unloaded.
UnloadSeg((Ptr) MyGestalt); // <12> ex<16> <Sys7.1> Unload Init
UnloadSeg((Ptr) _DataInit); // <12> Unload %A5Init
/* The event loop */
for (;;)
{
short thereIsAnEvent;
if (doneFlag)
CloseDACleanupAndExit(true);
/*
* Handle the next event.
*/
/* Anybody out there? */
thereIsAnEvent = WaitNextEvent(everyEvent, &myEvent, MAXSLEEPTIME, nil);
/* NOTE: This should be done after _WNE also, in case of DAs like Intermail
* that close themselves on the fly (in _SystemTask). We do the processing
* after waking up, which is now.
*/
if (doneFlag)
CloseDACleanupAndExit(true);
/* check for timeout */
if (!thereIsAnEvent || myEvent.what == nullEvent)
continue;
switch (myEvent.what)
{
case mouseDown:
switch (FindWindow(myEvent.where, &whichWindow))
{
case inSysWindow:
SystemClick(&myEvent, whichWindow);
break;
case inMenuBar:
DoCommand(MenuSelect(myEvent.where));
break;
}
break;
case keyDown:
if (myEvent.modifiers & cmdKey)
DoCommand(MenuKey((char)myEvent.message));
else
{
unsigned char key;
unsigned char virtualKey;
key = (char) myEvent.message;
virtualKey = (myEvent.message & keyCodeMask) >> 8;
if (key == kFunctionKey)
{
switch (virtualKey)
{
case kUndo:
key = 'Z';
break;
case kCut:
key = 'X';
break;
case kCopy:
key = 'C';
break;
case kPaste:
key = 'V';
break;
default:
key = 0;
break;
}
if (key) // if we mapped it then menukey
DoCommand(MenuKey(key));
}
}
break;
case diskEvt:
if (*((short *)&myEvent.message) != noErr)
(void)DIBadMount(bmPt, myEvent.message);
break;
case app4Evt:
break;
/* default:
* break;
*/
}
}
}
/*
* Initialization code: unload this segment when done with it.
*/
#pragma segment Init
/* InitConfig. Find out what sort of software/hardware setup we're dealing with.
* NOTE: This is the place to make any costly determinations about our environment
* for later time savings. None such, yet.
*/
void
InitConfig(void)
{
long qdInfo;
Colorized = false;
Has32BitQD = false;
if (MyGestalt(gestaltQuickdrawVersion,&qdInfo) == noErr) // ex<16> <Sys7.1>
{
qdInfo &= 0xFFFF;
Colorized = (qdInfo >= gestalt8BitQD);
Has32BitQD = (qdInfo >= gestalt32BitQD);
}
}
/* SetupMenus. Set up the Apple, and Edit menus.
* NOTE: If the MENU resources are missing, we die.
*/
void
SetupMenus(void)
{
extern MenuHandle MyMenus[];
register MenuHandle *pMenu;
Handle prevTop;
short topFile;
/* Set up the desk accessories menu. The "About…" item, followed by a grey
* line, is presumed to be already in the resource. We then append the desk
* accessory names from the 'DRVR' resources. We copy the menu because
* each DA Handle needs its own version of the common menu resource. HandToHand
* into the application heap means that dispose is automatic, even on crash.
* NOTE: We have to sidestep the DA file itself, since it has DRVR resource(s) and
* AddResMenu always uses the whole chain.
*/
MyMenus[appleMenu] = GetMenu(-appleID);
HandToHand(&MyMenus[appleMenu]);
topFile = CURMAP;
prevTop = TOPMAPHANDLE;
CURMAP = SYSMAP;
TOPMAPHANDLE = GetOverrideMap(SYSMAPHANDLE);
AddResMenu(MyMenus[appleMenu], (ResType) 'DRVR');
TOPMAPHANDLE = prevTop;
CURMAP = topFile;
/*
* Now File, Edit menus.
*/
MyMenus[fileMenu] = GetMenu(-fileID);
MyMenus[editMenu] = GetMenu(-editID);
/*
* Now insert all of the application menus in the menu bar.
*/
for (pMenu = &MyMenus[0]; pMenu < &MyMenus[menuCount]; ++pMenu)
InsertMenu(*pMenu, 0);
DrawMenuBar();
return;
}
void
StartupCode(void)
{
/* Clear global flag that tells us when to quit */
doneFlag = 0;
/* Initialization traps, pretty much required */
MaxApplZone();
InitConfig();
InitGraf(&qd.thePort);
InitFonts();
InitWindows();
InitMenus();
TEInit();
InitDialogs(nil);
InitCursor();
FlushEvents(everyEvent, 0);
myDriverRefNum = 0;
myUnitNumber = 0;
SetupMenus();
/* Patch _Close to check for closing desk accessories */
oldCloseAddress = (Ptr)NGetTrapAddress((short) _Close, OSTrap);
NSetTrapAddress((long)ClosePatch, (short) _Close, OSTrap);
/* Patch _SystemEvent so we can handle cmd-Q and cmd-W */
oldSystemEventAddress = (Ptr)NGetTrapAddress((short) _SystemEvent, ToolTrap);
NSetTrapAddress((long)SystemEventPatch, (short) _SystemEvent, ToolTrap);
/* Patch _ExitToShell to check for closing desk accessories, so that
* we can handle ES from MacsBug.
*/
oldExitToShellAddress = (Ptr)NGetTrapAddress((short) _ExitToShell, ToolTrap);
NSetTrapAddress((long)ExitToShellPatch, (short) _ExitToShell, ToolTrap);
}
/* GetRsrcHdlMatchingFirstLongword. Find the first resource of specified type that
* has the given value in its first long word. Scope is current file.
*/
Handle
GetRsrcHdlMatchingFirstLongword(long resType, long firstLong)
{
Handle hdl;
short resIndex, resLast;
resLast = Count1Resources(resType);
for (resIndex = 1; resIndex <= resLast; resIndex++)
{
hdl = Get1IndResource(resType, resIndex);
if (hdl != nil)
{
if (**((long **) hdl) == firstLong)
return (hdl);
ReleaseResource(hdl);
}
}
return (nil);
}
/* GetIconIDFromBundle. Figure out the icon family resource ID for the given type and
* creator. Starts at the current resource map. Uses the same search path that Finder
* does (FREF -> BNDL -> icon resource).
*/
short
GetIconIDFromBundle(OSType theType, unsigned long theCreator)
{
Ptr pRsrc;
short typeIndex, icnIndex;
Handle rsrcHdl;
short localID, globalID;
/* Find the FREF of the type */
rsrcHdl = GetRsrcHdlMatchingFirstLongword('FREF', theType);
if (rsrcHdl == nil)
return(0);
pRsrc = *rsrcHdl + 4;
localID = *((short *) pRsrc);
ReleaseResource(rsrcHdl);
/* Get the BNDL */
rsrcHdl = GetRsrcHdlMatchingFirstLongword('BNDL', theCreator);
if (rsrcHdl == nil)
return(0);
pRsrc = *rsrcHdl + 6;
/* Major loop -- find global id of icon in bundle */
globalID = 0;
for (typeIndex = *((short *) pRsrc)++; typeIndex >= 0; typeIndex--)
{
/* Is this type ICN# ? */
if (*((long *) pRsrc)++ == 'ICN#')
{
/* Minor loop - find a match for our given local ID and get it */
for (icnIndex = *((short *) pRsrc)++; icnIndex >= 0; icnIndex--)
{
if (*((short *) pRsrc)++ == localID)
{
/* Found it! */
globalID = *((short *) pRsrc);
break;
}
pRsrc += 2;
}
/* Either we found it or we looked at all icons */
break;
}
else
{
/* Skip over all of this type's resources, carefully because count
* is zero-based.
*/
((long *) pRsrc) += *((short *) pRsrc)++;
((long *) pRsrc)++;
}
}
/* Clean up and go */
ReleaseResource(rsrcHdl);
return(globalID);
}
/* GetDisplayIconID. Return the resource ID of the DA's icon family. */
short
GetDisplayIconID(void)
{
ProcessInfoRec procInfo;
short retval;
FInfo finderInfo;
FSSpec fileSpec;
/* Nothing special if in the system file or an override map */
if (IsThisASystemResourceMap(TOPMAPHANDLE))
return(genericDeskAccessoryIconResource);
/* Get process info for search */
procInfo.processInfoLength = sizeof(ProcessInfoRec);
procInfo.processName = nil;
procInfo.processAppSpec = &fileSpec;
SetPSN(kCurrentProcess, &(procInfo.processNumber));
if (GetProcessInformation(&procInfo.processNumber, &procInfo) != noErr)
return(genericDeskAccessoryIconResource);
/* Try for custom icon first. If not custom, use our type and signature in
* BNDL lookup.
*/
if ((FSpGetFInfo(&fileSpec, &finderInfo) == noErr) && ((finderInfo.fdFlags & FILE_FLAG_HAS_CUSTOM_ICON) != 0))
retval = kCustomIconID;
else if ((retval = GetIconIDFromBundle(procInfo.processType, procInfo.processSignature)) == 0)
retval = genericDeskAccessoryIconResource;
return(retval);
}
/* ID of owned resources for the "about me" dialog */
#define OWN_ABOUT_ME_ID (1)
#define OWN_DLOG_PROTO (OWNED_BY_DRVR | OWN_ABOUT_ME_ID)
#define STANDARD_DLOG_WIDTH (260)
/* InitAboutMeDialog. Figure out the strings and offsets we'll need to set up the
* "about me" dialog. The dialog has (among other things) two static text items
* positioned one atop the other. The texts are supplied by resources, and both
* are optional. If just one is missing, the other goes in the top position. Sets
* the hFirstAboutMeString, hSecondAboutMeString, wdShrinkOfAboutMe, htOfAboutMeStrings,
* htOfFirstAboutString, and htOfSecondAboutString.
*/
void
InitAboutMeDialog(void)
{
DialogRecord dialogRecord;
THEZONE = APPLZONE;
/* Set defaults in case we don't want or can't get everything */
wdShrinkOfAboutMe = 0;
htOfAboutMeStrings = 0;
htOfFirstAboutString = 0;
htOfSecondAboutString = 0;
hFirstAboutMeString = nil;
hSecondAboutMeString = nil;
/* Determine resource ID of display icon. Use it to locate the info strings. */
if ((myDisplayIconID = GetDisplayIconID()) != genericDeskAccessoryIconResource)
hFirstAboutMeString = Get1Resource('dast', myDisplayIconID);
/* If DA is from the system file, get the informative string */
if ((hSecondAboutMeString = (IsSystemRefNum(myFileRefNum)) ? GetResource('STR ', StrID_InSysFile) : nil) != nil)
{
DetachResource(hSecondAboutMeString);
HNoPurge(hSecondAboutMeString);
}
/* Determine the lineup */
if (hFirstAboutMeString == nil)
{
hFirstAboutMeString = hSecondAboutMeString;
hSecondAboutMeString = nil;
}
else
HandToHand(&hFirstAboutMeString);
if (GetNewDialog(aboutMeDLOG, &dialogRecord, (WindowPtr) -1) != nil)
{
if (hFirstAboutMeString != nil)
{
short itemType;
Handle itemHdl;
Rect itemRect1, itemRect2;
short lineHeight;
register StringHandle hStr;
GrafPtr savePort;
GetPort(&savePort);
SetPort(&dialogRecord);
GetDItem(&dialogRecord, secondAboutString, &itemType, &itemHdl, &itemRect2);
GetDItem(&dialogRecord, firstAboutString, &itemType, &itemHdl, &itemRect1);
itemRect1.bottom = itemRect1.top+100;
SetDItem(&dialogRecord, firstAboutString, editText, itemHdl, &itemRect1);
lineHeight = (*dialogRecord.textH)->lineHeight;
hStr = hFirstAboutMeString;
do
{
short numLines;
HLock(hStr);
SetIText(itemHdl, *hStr);
HUnlock(hStr);
numLines = (*dialogRecord.textH)->nLines;
if (hStr != hFirstAboutMeString)
{
htOfSecondAboutString = numLines * lineHeight;
htOfAboutMeStrings += (itemRect2.top - itemRect1.top) + htOfSecondAboutString;
break;
}
htOfAboutMeStrings = htOfFirstAboutString = numLines * lineHeight;
hStr = hSecondAboutMeString;
} while (hStr != nil);
SetPort(savePort);
}
wdShrinkOfAboutMe = (htOfAboutMeStrings != 0) ? 0 : dialogRecord.window.port.portRect.right - dialogRecord.window.port.portRect.left - STANDARD_DLOG_WIDTH;
CloseDialog(&dialogRecord);
}
else
{
hFirstAboutMeString = nil;
hSecondAboutMeString = nil;
}
THEZONE = SYSZONE;
}
/* PutUpDeathAlert. Put up an alert, if there's enough memory. Try to get the memory
* out of the DA Handler app heap, which should be unaffected by the ravages of what is
* now a very small sys heap.
*/
void
PutUpDeathAlert(OSErr err)
{
Handle slopHdl;
short alertID;
THEZONE = APPLZONE;
alertID = (err == memFullErr) ? AlertID_OutOfMem : AlertID_NotFound;
if (GetResource('ALRT', alertID) == nil
|| GetResource('DITL', alertID) == nil
|| (slopHdl = NewHandle(ALERT_SLOP_SIZE)) == nil)
{
SysBeep(20);
return;
}
DisposHandle(slopHdl);
ParamText(CURAPNAME, nil, nil, nil);
(void)StopAlert(alertID, nil);
THEZONE = SYSZONE;
}
typedef pascal unsigned long (*GzProcPtr)(unsigned long cbNeeded);
/* Without doing actual purging, guarantee there are cbNeeded free or purgeable bytes
* in THEZONE, including attempts to grow the heap.
*/
Boolean
CanGetThisMuchFreeOrPurgeSpace(long cbNeeded)
{
GzProcPtr gzProcPtr = SYSZONE->gzProc;
for (;;)
{
/* Quit now if enough contiguous space already exists */
if (SysContigPurgeSpace() >= cbNeeded)
return true;
/* See how much the growzone procedure buys us */
if (gzProcPtr == nil || (*gzProcPtr)(cbNeeded) == 0)
return false;
}
}
/* IsDrvrOpen. Return boolean whether our driver is open. Assumes myUnitNumber. */
Boolean
IsDrvrOpen(void)
{
DCtlEntry **ppDCtlEntry;
ppDCtlEntry = ((DCtlEntry ***)UNITTABLE)[myUnitNumber];
return (ppDCtlEntry != nil && (((*ppDCtlEntry)->dCtlFlags & DOpened) != 0));
}
#define DA_SLOP_SIZE 0x2000 /* Min memory a DA need to operate in (a guess) */
#define TOOLBOX_SLOP_BASE 0x5000 /* Always require at least this much */
#define TOOLBOX_SLOP_32QD 0x4000 /* Add this much if toolbox has 32-bit QuickDraw */
#define TOOLBOX_SLOP_COLOR 0x5000 /* Add this much if toolbox has non-32-bit color */
/* EstimatedToolboxHeap. Because the DA will be calling toolbox routines using the
* system heap, if the toolbox takes too much memory, other DAs and/or applications
* will die. We therefore preflight the system heap. The required amount is hard to
* predict, but is strongly related to the toolbox capabalitities. This routine makes
* its estimate by starting at a value for the fundamental toolbox, then adding a weight
* representative of each capability we have found to be a memory hog.
* For now, it is felt that color quickdraw is the only thing that greatly
* contributes. As more hogs are born, check for them here and add an appropriate
* amount if they exist.
*/
long
EstimatedToolboxHeap(void)
{
long requirement;
requirement = TOOLBOX_SLOP_BASE;
/* Color always requires more, but 32-bit QuickDraw is lesser of two evils */
if (Has32BitQD)
requirement += TOOLBOX_SLOP_32QD;
else
{
if (Colorized)
requirement += TOOLBOX_SLOP_COLOR;
}
return requirement;
}
/* PreflightDA. Determine if there is enough room for a DA in the system heap. */
OSErr
PreflightDA(void)
{
Handle drvrHdl;
Handle slopHdl;
long flags;
Boolean gotSpace;
/* Find the resource. Return error if not found, or not enough room for it. */
if ((drvrHdl = GetNamedResource('DRVR',CURAPNAME)) == nil)
return RESERR;
/* Take a whack at one of our globals */
myFileRefNum = HomeResFile(drvrHdl);
/* Found the resource, so determine unit table index from resource ID.
* NOTE: Assumes the best even if we couldn't get resource info.
*/
ROMMAPINSERT = -1;
GetResInfo(drvrHdl, &myUnitNumber, nil, nil);
if (RESERR != noErr)
return noErr;
/* Base the preliminary settings of the unit number and refnum globals on the
* resource ID returned by GetResInfo. These will get reset after OpenDeskAcc. We
* need them now so that we can handle an ExitToShell from inside OpenDeskAcc. If
* the OpenDeskAcc got far enough, the driver will be open and need to be closed.
*/
myDriverRefNum = (-(myUnitNumber + 1));
/* No space problem if the driver is already open */
if (IsDrvrOpen())
return(noErr);
/* Error if there wasn't space for driver. */
if (*drvrHdl == nil)
return(memFullErr);
/* Make sure there's enough room for everything else, too. But don't let the
* driver get purged by the check.
*/
gotSpace = false;
flags = HGetState(drvrHdl);
HNoPurge(drvrHdl);
if ((slopHdl = NewHandle(DA_SLOP_SIZE)) != nil)
{
gotSpace = CanGetThisMuchFreeOrPurgeSpace(EstimatedToolboxHeap());
DisposHandle(slopHdl);
}
HSetState(drvrHdl, flags);
return (gotSpace ? noErr : memFullErr);
}
/* OpenTheDeskAccessory. Routine to call when we're ready to open the DA. */
void
OpenTheDeskAccessory(void)
{
GrafPtr savePort;
short odaResult;
MenuHandle appleMenuHdl;
StringHandle tempStrHdl;
unsigned char ParamZeroStr[3] = {2, '^','0'};
StringPtr ptr1, ptr2;
unsigned long len1, len2;
OSErr err;
Str255 strItemName;
/* Go to the top of the chain */
CURMAP = TopResFile();
/* Cancel operation if there is not enough memory to work with. */
if ((err = PreflightDA()) != noErr)
{
PutUpDeathAlert(err);
doneFlag = true;
return;
}
/* Put DA name in the place of honor */
appleMenuHdl = MyMenus[appleMenu];
GetItem(appleMenuHdl, aboutMeCommand, &strItemName);
if ((tempStrHdl = NewString(strItemName)) != nil)
{
ptr1 = (StringPtr) &ParamZeroStr;
len1 = (unsigned long) Length(ptr1);
ptr1 = &StringByte(ptr1, 0);
ptr2 = &StringByte(CURAPNAME, 1);
len2 = (unsigned long) Length(CURAPNAME) - 1;
(void) Munger(tempStrHdl, 0, ptr1, len1, ptr2, len2);
Length(*tempStrHdl) += len2 - len1;
if (MEMERROR == noErr)
{
HLock(tempStrHdl);
SetItem(appleMenuHdl, aboutMeCommand, *tempStrHdl);
HUnlock(tempStrHdl);
}
DisposHandle(tempStrHdl);
}
/* Do actual open */
GetPort(&savePort);
odaResult = OpenDeskAcc(CURAPNAME);
SetPort(savePort);
/* Check whether open failed. */
if (isNotDARefNum(odaResult))
{
doneFlag = true;
return;
}
/* Base the permanent settings of the unit number and refnum globals on the
* OpenDeskAcc function result.
*/
myDriverRefNum = odaResult;
myUnitNumber = (-(myDriverRefNum + 1));
/* Check whether DA completed already. */
if (IsDrvrOpen() == false)
{
doneFlag = true;
return;
}
/* Set up the about box dialog. */
InitAboutMeDialog();
}
#pragma segment Main
pascal void
MyItemProc(WindowPtr theWindow, short itemNum)
{
short typ;
Handle hdl;
Rect theRect;
GrafPtr savePort;
/* Frame the OK button */
GetDItem(theWindow, okButton, &typ, &hdl, &theRect);
PenSize(3, 3);
InsetRect(&theRect, -4, -4);
FrameRoundRect(&theRect, 16, 16);
/* Plot the icon */
GetPort(&savePort);
SetPort(theWindow);
GetDItem(theWindow, itemNum, &typ, &hdl, &theRect);
PlotIconID(&theRect, atAbsoluteCenter, ttNone, myDisplayIconID);
SetPort(savePort);
}
/* showAboutMeDialog. Display the DAHandler Application dialog. */
void
showAboutMeDialog(void)
{
GrafPtr savePort;
DialogRecord dialogRecord;
Handle hDLOG;
short itemType;
Handle itemHdl;
Rect itemRect;
short itemHit;
THEZONE = APPLZONE;
GetPort(&savePort);
/* Pre-adjust dialog size for optional parts */
{
Rect *pRect;
if ((hDLOG = GetResource('DLOG', aboutMeDLOG)) == nil)
return;
HNoPurge(hDLOG);
pRect = (Rect *) *hDLOG;
pRect->right -= wdShrinkOfAboutMe;
pRect->bottom += htOfAboutMeStrings;
}
/* Allocate a dialog record */
if (GetNewDialog(aboutMeDLOG, &dialogRecord, (WindowPtr) -1) == nil)
return;
/* Release the resource so that resizing change will not be remembered */
ReleaseResource(hDLOG);
/* Display dialog if there's still enough room */
if ((hDLOG = NewHandle(ALERT_SLOP_SIZE)) != nil)
{
short pixelsNeeded;
DisposHandle(hDLOG);
SetPort(&dialogRecord);
AutoPositionWindow((WindowPtr) &dialogRecord, lcMainScreen, hcCenter, vcAlertCenter);
/* Set the user item proc address into the icon item */
GetDItem(&dialogRecord, daIcon, &itemType, &itemHdl, &itemRect);
SetDItem(&dialogRecord, daIcon, itemType, (Handle) &MyItemProc, &itemRect);
/* Set the DA name */
GetDItem(&dialogRecord, daNameItem, &itemType, &itemHdl, &itemRect);
SetIText(itemHdl, (StringPtr)CURAPNAME);
/* Adjust the first text item length */
if ((pixelsNeeded = htOfAboutMeStrings) != 0)
{
GetDItem(&dialogRecord, firstAboutString, &itemType, &itemHdl, &itemRect);
itemRect.bottom = itemRect.top + htOfFirstAboutString;
SetDItem(&dialogRecord, firstAboutString, itemType, itemHdl, &itemRect);
HLock(hFirstAboutMeString);
SetIText(itemHdl, *hFirstAboutMeString);
HUnlock(hFirstAboutMeString);
/* Adjust the second text item vertical position and length */
if (hSecondAboutMeString != nil)
{
GetDItem(&dialogRecord, secondAboutString, &itemType, &itemHdl, &itemRect);
itemRect.top += htOfFirstAboutString;
itemRect.bottom = itemRect.top + htOfSecondAboutString;
SetDItem(&dialogRecord, secondAboutString, itemType, itemHdl, &itemRect);
HLock(hSecondAboutMeString);
SetIText(itemHdl, *hSecondAboutMeString);
HUnlock(hSecondAboutMeString);
}
}
/* Adjust the position of the OK button */
GetDItem(&dialogRecord, okButton, &itemType, &itemHdl, &itemRect);
itemRect.bottom += pixelsNeeded;
itemRect.left -= wdShrinkOfAboutMe;
itemRect.top += pixelsNeeded;
itemRect.right -= wdShrinkOfAboutMe;
MoveControl(itemHdl, itemRect.left, itemRect.top);
SetDItem(&dialogRecord, okButton, itemType, itemHdl, &itemRect);
/* Get this show on the road!
* NOTE: Must switch back to system heap since ModalDialog and/or WDEF may load
* resources or allocate memory.
*/
ShowWindow(&dialogRecord);
THEZONE = SYSZONE;
ModalDialog(nil, &itemHit);
THEZONE = APPLZONE;
SetPort(savePort);
}
CloseDialog(&dialogRecord);
if (dialogRecord.items != nil)
DisposHandle(dialogRecord.items);
/* Back to the DA zone */
THEZONE = SYSZONE;
return;
}
/*
* Process mouse clicks in menu bar
*/
void
DoCommand(long mResult)
{
extern MenuHandle MyMenus[];
short theMenu, theItem;
Str255 daName;
theItem = LOWORD(mResult);
theMenu = HIWORD(mResult); /* This is the resource ID */
switch (theMenu)
{
case appleID:
if (theItem == aboutMeCommand)
showAboutMeDialog();
else
{
GrafPtr savePort;
GetItem(MyMenus[appleMenu], theItem, &daName);
GetPort(&savePort);
(void) OpenDeskAcc(&daName);
SetPort(savePort);
}
break;
case fileID:
switch (theItem)
{
case closeCommand:
CloseFrontDeskWindow();
break;
case quitCommand:
CloseDACleanupAndExit(true);
break;
}
break;
case editID:
/*
* If this is for a 'standard' edit item,
* run it through SystemEdit first.
* SystemEdit will return FALSE if it's not a system window.
*/
SystemEdit(theItem-1);
}
HiliteMenu(0);
}
/* HandleCloseDA. Routine to be called from our Close patch. Shuts down the
* DA if the DA is asking to close its driver code.
*/
void
HandleCloseDA(short daRefNum)
{
ProcessSerialNumber myPSN;
if (daRefNum == myDriverRefNum)
{
SetPSN(kCurrentProcess, &myPSN);
(void) WakeUpProcess(&myPSN);
doneFlag = true;
}
}