Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
G4Utilities.c
/* |
File: G4Utilities.c |
Contains: xxx put contents here xxx |
Version: xxx put version here xxx |
Copyright: © 1998 by Apple Computer, Inc., all rights reserved. |
File Ownership: |
DRI: xxx put dri here xxx |
Other Contact: xxx put other contact here xxx |
Technology: xxx put technology here xxx |
Writers: |
(sjb) Steve Bollinger |
Change History (most recent first): |
<2> 7/1/98 sjb Update to CWPro 2 |
*/ |
//============================================================================ |
//---------------------------------------------------------------------------- |
// Utilities.c |
//---------------------------------------------------------------------------- |
//============================================================================ |
// These functions are sort of universal utility functions. They aren't specificÉ |
// to Glypha per se. I use these (and others) in many, many games. Many of themÉ |
// as well are useful for any app you might write for the Mac. |
#include "G4Externs.h" |
#include <Processes.h> |
#include <SegLoad.h> |
#include <Traps.h> |
#include <Resources.h> |
#include <TextUtils.h> |
#include <LowMem.h> |
#define kActive 0 |
#define kInactive 255 |
GDHandle thisGDevice; |
long tickNext; |
//============================================================== Functions |
//-------------------------------------------------------------- RandomInt |
// Takes a short (range) and returns a random number from zero to range - 1. |
short RandomInt (short range) |
{ |
register long rawResult; |
rawResult = Random(); |
if (rawResult < 0L) |
rawResult *= -1L; |
rawResult = (rawResult * (long)range) / 32768L; |
return ((short)rawResult); |
} |
//-------------------------------------------------------------- RedAlert |
// Generic error function. This is called when there is no hope of recovering |
// from the error. A simple alert is brought up and the text passed in (theStr) |
// is displayed. When the user clicks the Okay button, we quit to the Finder. |
void RedAlert (StringPtr theStr) |
{ |
#define kRedAlertID 128 |
short whoCares; |
#if GENERATINGPOWERPC |
// make sure we are at full color |
DSpContext_FadeGammaIn( NULL, NULL ); |
if( gTheContext ) |
{ |
DSpContext_SetState( gTheContext, kDSpContextState_Inactive ); |
DSpContext_Release( gTheContext ); |
DSpShutdown(); |
} |
#endif |
ParamText(theStr, "\p", "\p", "\p"); // Replace ^0 in alert with error mssg. |
whoCares = Alert(kRedAlertID, 0L); // Bring up alert. |
ExitToShell(); // Quit to Finder. |
} |
//-------------------------------------------------------------- FindOurDevice |
// Get a handle to the MainDevice (monitor with the Menubar). |
void FindOurDevice (void) |
{ |
thisGDevice = GetMainDevice(); |
if (thisGDevice == 0L) // If a nil handle is returned... |
RedAlert("\pCouldn't Find Our Device"); // call our universal error alert. |
} |
//-------------------------------------------------------------- LoadGraphic |
// Handy function that loads a PICT graphic, get's its bounds and draws it. |
// The port drawn to is assumed the current port. No scaling is done. |
void LoadGraphic (short resID) |
{ |
Rect bounds; |
PicHandle thePicture; |
thePicture = GetPicture(resID); // Load graphic from resource fork. |
if (thePicture == 0L) // Check to see if nil (did it load?) |
RedAlert("\pA Graphic Couldn't Be Loaded"); |
HLock((Handle)thePicture); // If we made it this far, lock handle. |
bounds = (*thePicture)->picFrame; // Get a copy of the picture's bounds. |
HUnlock((Handle)thePicture); // We can unlock the picture now. |
OffsetRect(&bounds, -bounds.left, -bounds.top); // Offset bounds rect to (0, 0). |
DrawPicture(thePicture, &bounds); // Draw picture to current port. |
ReleaseResource((Handle)thePicture); // Dispose of picture from heap. |
} |
//-------------------------------------------------------------- CreateOffScreenPixMap |
// Handles the creation of an offscreen pixmap. Depth is assumed to be that of theÉ |
// current gDevice. If the allocation fails (low memory, etc.) we quit to Finder. |
void CreateOffScreenPixMap (Rect *theRect, CGrafPtr *offScreen) |
{ |
CTabHandle thisColorTable; |
GDHandle oldDevice; |
CGrafPtr newCGrafPtr; |
Ptr theseBits; |
long sizeOfOff, offRowBytes; |
OSErr theErr; |
short thisDepth; |
oldDevice = GetGDevice(); |
SetGDevice(thisGDevice); |
newCGrafPtr = 0L; |
newCGrafPtr = (CGrafPtr)NewPtrClear(sizeof(CGrafPort)); |
if (newCGrafPtr != 0L) |
{ |
OpenCPort(newCGrafPtr); |
thisDepth = (**(*newCGrafPtr).portPixMap).pixelSize; |
offRowBytes = ((((long)thisDepth * |
(long)(theRect->right - theRect->left)) + 15L) >> 4L) << 1L; |
sizeOfOff = (long)(theRect->bottom - theRect->top) * offRowBytes; |
OffsetRect(theRect, -theRect->left, -theRect->top); |
theseBits = NewPtr(sizeOfOff); |
if (theseBits != 0L) |
{ |
(**(*newCGrafPtr).portPixMap).baseAddr = theseBits; |
(**(*newCGrafPtr).portPixMap).rowBytes = (short)offRowBytes + 0x8000; |
(**(*newCGrafPtr).portPixMap).bounds = *theRect; |
thisColorTable = (**(**thisGDevice).gdPMap).pmTable; |
theErr = HandToHand((Handle *)&thisColorTable); |
(**(*newCGrafPtr).portPixMap).pmTable = thisColorTable; |
ClipRect(theRect); |
RectRgn(newCGrafPtr->visRgn, theRect); |
ForeColor(blackColor); |
BackColor(whiteColor); |
EraseRect(theRect); |
} |
else |
{ |
CloseCPort(newCGrafPtr); |
DisposePtr((Ptr)newCGrafPtr); |
newCGrafPtr = 0L; |
RedAlert("\pCouldn't Allocate Enough Memory"); |
} |
} |
else |
RedAlert("\pCouldn't Allocate Enough Memory"); |
*offScreen = newCGrafPtr; |
SetGDevice(oldDevice); |
} |
//-------------------------------------------------------------- CreateOffScreenBitMap |
// Creates an offscreen bitmap. Depth is of course 1 (b & w). If this functionÉ |
// fails to create the bitmap, we post an alert and quit to the Finder. |
void CreateOffScreenBitMap (Rect *theRect, GrafPtr *offScreen) |
{ |
GrafPtr theBWPort; |
BitMap theBitMap; |
long theRowBytes; |
theBWPort = (GrafPtr)(NewPtr(sizeof(GrafPort))); |
OpenPort(theBWPort); |
theRowBytes = (long)((theRect->right - theRect->left + 15L) / 16L) * 2L; |
theBitMap.rowBytes = (short)theRowBytes; |
theBitMap.baseAddr = NewPtr((long)theBitMap.rowBytes * |
(theRect->bottom - theRect->top)); |
if (theBitMap.baseAddr == 0L) |
RedAlert("\pCouldn't Create Bitmaps"); |
theBitMap.bounds = *theRect; |
if (MemError() != noErr) |
RedAlert("\pCouldn't Create Bitmaps"); |
SetPortBits(&theBitMap); |
ClipRect(theRect); |
RectRgn(theBWPort->visRgn, theRect); |
EraseRect(theRect); |
*offScreen = theBWPort; |
} |
//-------------------------------------------------------------- ZeroRectCorner |
// Offset rect to (0, 0). This means the upper left corner of the rect is |
// moved to the origin - to (0, 0) - to the upperleft corner of the port. |
void ZeroRectCorner (Rect *theRect) |
{ |
theRect->right -= theRect->left; // Move right edge by amount of left. |
theRect->bottom -= theRect->top; // Move bottom edge by amount of top. |
theRect->left = 0; // Can now set left to zero. |
theRect->top = 0; // Can set top edge to zero as well. |
} |
//-------------------------------------------------------------- FlashShort |
// This is a simple debugging function that will display the short passed to itÉ |
// in the upper left corner of the screen. It's a handy way to watch the valueÉ |
// of a variable while the program is running. |
void FlashShort (short theValue) |
{ |
GrafPtr wasPort, tempPort; |
Str255 tempStr; |
Rect tempRect; |
GetPort(&wasPort); // Remember old grafPort. |
tempPort = (GrafPtr)NewPtrClear(sizeof(GrafPort)); |
OpenPort(tempPort); // Create a new empty port. |
NumToString((long)theValue, tempStr); // Convert value passed in to a string. |
MoveTo(20, 40); // Move the pen to the upperleft corner. |
SetRect(&tempRect, 18, 20, 122, 42); // Create a rect up there as well. |
EraseRect(&tempRect); // Erase the rect (to make a white hole). |
DrawString(tempStr); // And draw our text into that hole. |
ClosePort(tempPort); // Get rid of out temp port. |
SetPort((GrafPtr)wasPort); // And set port back to the old one. |
} |
//-------------------------------------------------------------- LogNextTick |
// Simple function to set a global (tickNext) to the current TickCount() plusÉ |
// some offset. We'll then wait for TickCount() to exceed that global. We useÉ |
// this function and the function below to regulate animation speeds (rememberÉ |
// your game may be run on a slow Mac or a fast one - we need a way to keep theÉ |
// motion consistent. I love when the comments are longer than the function. |
// (Not really.) |
void LogNextTick (long howMany) |
{ |
tickNext = TickCount() + howMany; // Get machine's TickCount() and add to it. |
} |
//-------------------------------------------------------------- WaitForNextTick |
// This is the companion function to the above function (LogNextTick()). |
// We do nothing but loop until TickCount() catches up with (or passes) ourÉ |
// global variable tickNext. |
void WaitForNextTick (void) |
{ |
do |
{ |
} |
while (TickCount() < tickNext); // Loop until TickCount() catches up. |
} |
//-------------------------------------------------------------- TrapExists |
// A nice "test function" that test for the existence of some ToolBox trap. |
// Returns TRUE if the function exists, FALSE if it doesn't. |
Boolean TrapExists (short trapNumber) |
{ |
#define kUnimpTrap 0x9F |
// Test trap number against unimplemented trap number. |
return ((NGetTrapAddress(trapNumber, ToolTrap) != |
NGetTrapAddress(kUnimpTrap, ToolTrap))); |
} |
//-------------------------------------------------------------- DoWeHaveGestalt |
// This function specifically tests for the availablity of the Gestalt() function. |
// It returns TRUE if Gestalt() exists, FALSE if it doesn't. |
Boolean DoWeHaveGestalt (void) |
{ |
#define kGestaltTrap 0xAD |
// Call above function (TrapExists()) with the Gestalt() trap number. |
return (TrapExists(kGestaltTrap)); |
} |
//-------------------------------------------------------------- CenterAlert |
// Handy function to center any alert within the main monitor. |
void CenterAlert (short alertID) |
{ |
AlertTHndl alertHandle; |
Rect theScreen, alertRect; |
short horiOff, vertOff; |
Byte wasState; |
theScreen = qd.screenBits.bounds; // Get main monitor's bounds. |
theScreen.top += LMGetMBarHeight(); // Account for menubar height. |
// Get handle to alert resource. |
alertHandle = (AlertTHndl)GetResource('ALRT', alertID); |
if (alertHandle != 0L) // Make sure we got it! |
{ // Remember its "state" (locked, etc.) |
wasState = HGetState((Handle)alertHandle); |
HLock((Handle)alertHandle); // We'll lock it. |
// Get a copy of it's bounds and zero. |
alertRect = (**alertHandle).boundsRect; |
OffsetRect(&alertRect, -alertRect.left, -alertRect.top); |
// Calculate offsets for centering bounds. |
horiOff = ((theScreen.right - theScreen.left) - alertRect.right) / 2; |
vertOff = ((theScreen.bottom - theScreen.top) - alertRect.bottom) / 3; |
// And offset the bounds copy. |
OffsetRect(&alertRect, horiOff, vertOff + LMGetMBarHeight()); |
// Set alerts bounds to our centered rect. |
(**alertHandle).boundsRect = alertRect; |
HSetState((Handle)alertHandle, wasState); |
} |
} |
//-------------------------------------------------------------- RectWide |
// Handy function for returning the absolute width of a rectangle. |
short RectWide (Rect *theRect) |
{ |
return (theRect->right - theRect->left); |
} |
//-------------------------------------------------------------- RectTall |
// Handy function for returning the absolute height of a rectangle. |
short RectTall (Rect *theRect) |
{ |
return (theRect->bottom - theRect->top); |
} |
//-------------------------------------------------------------- CenterRectInRect |
// Nice utility function that takes two rectangles and centers the firstÉ |
// rectangle within the second. |
void CenterRectInRect (Rect *rectA, Rect *rectB) |
{ |
short widthA, tallA; |
widthA = RectWide(rectA); // Get width of 1st rect. |
tallA = RectTall(rectA); // Get height of 1st rect. |
// Do the math (center horizontally). |
rectA->left = rectB->left + (RectWide(rectB) - widthA) / 2; |
rectA->right = rectA->left + widthA; |
// Do the math (center vertically). |
rectA->top = rectB->top + (RectTall(rectB) - tallA) / 2; |
rectA->bottom = rectA->top + tallA; |
} |
//-------------------------------------------------------------- PasStringCopy |
// This is a nice function that helps to free you from dealing with C strings. |
// It takes one Pascal-style string and copies it to a second. |
void PasStringCopy (StringPtr p1, StringPtr p2) |
{ |
register short stringLength; |
stringLength = *p2++ = *p1++; // Get 1st string's length. |
while (--stringLength >= 0) // Loop through each character in 1st string. |
*p2++ = *p1++; // And copy to 2nd string. |
} |
//-------------------------------------------------------------- CenterDialog |
// Like CenterAlert(), this function centers a Dialog on the main monitor. |
void CenterDialog (short dialogID) |
{ |
DialogTHndl dlogHandle; |
Rect theScreen, dlogBounds; |
short hPos, vPos; |
Byte wasState; |
theScreen = qd.screenBits.bounds; // Get main monitor's bounds. |
theScreen.top += LMGetMBarHeight(); // Add menuBar's height. |
// Load up dialog from resource. |
dlogHandle = (DialogTHndl)GetResource('DLOG', dialogID); |
if (dlogHandle != 0L) // If it loaded....! |
{ // Remember handle state. |
wasState = HGetState((Handle)dlogHandle); |
HLock((Handle)dlogHandle); // We're going to lock it. |
// Get a copy of the dialog's bounds. |
dlogBounds = (**dlogHandle).boundsRect; |
OffsetRect(&dlogBounds, -dlogBounds.left, -dlogBounds.top); |
// Calculate how much to offset. |
hPos = ((theScreen.right - theScreen.left) - dlogBounds.right) / 2; |
vPos = ((theScreen.bottom - theScreen.top) - dlogBounds.bottom) / 3; |
// Offset ourt copy of the bounds. |
OffsetRect(&dlogBounds, hPos, vPos + LMGetMBarHeight()); |
// Set dlg's bounds to centered rect. |
(**dlogHandle).boundsRect = dlogBounds; |
HSetState((Handle)dlogHandle, wasState);// Restore handle's state. |
} |
} |
//-------------------------------------------------------------- DrawDefaultButton |
// A nice dialog function. This draws the bold default outline aroundÉ |
// item #1 in the dialog passed in. |
void DrawDefaultButton (DialogPtr theDialog) |
{ |
Rect itemRect; |
Handle itemHandle; |
short itemType; |
// Get at the item's bounds. |
GetDialogItem(theDialog, 1, &itemType, &itemHandle, &itemRect); |
InsetRect(&itemRect, -4, -4); // Inset (outset?) bounds by -4 pixels. |
PenSize(3, 3); // Set the pen 3 pixels thick. |
FrameRoundRect(&itemRect, 16, 16); // Draw the button outline. |
PenNormal(); // And restore pen to 1 pixel thick. |
} |
//-------------------------------------------------------------- PasStringCopyNum |
// Another function to keep you from using C strings. This one copies only aÉ |
// certain number of characters from one Pascal-style string to a second. |
void PasStringCopyNum (StringPtr p1, StringPtr p2, short charsToCopy) |
{ |
short i; |
if (charsToCopy > *p1) // If trying to copy more chars than there areÉ |
charsToCopy = *p1; // Reduce the number of chars to copy to this size |
*p2 = charsToCopy; // Set 2nd string's length to charsToCopy. |
*p2++; // Point to first character in 2nd string. |
*p1++; // Point to first character in 1st string. |
for (i = 0; i < charsToCopy; i++) |
*p2++ = *p1++; // Copy the specified number of chars over. |
} |
//-------------------------------------------------------------- GetDialogString |
// Handy dialog function that returns a dialog item string. This will beÉ |
// especially handy for getting the high score name the player enters. |
void GetDialogString (DialogPtr theDialog, short item, StringPtr theString) |
{ |
Rect itemRect; |
Handle itemHandle; |
short itemType; |
// Get handle to dialog item. |
GetDialogItem(theDialog, item, &itemType, &itemHandle, &itemRect); |
GetDialogItemText(itemHandle, theString); // Extract text from item handle. |
} |
//-------------------------------------------------------------- SetDialogString |
// Like the above function, but this one sets a dialog items string to whateverÉ |
// you pass in. We'll use this to set a default high score name. |
void SetDialogString (DialogPtr theDialog, short item, StringPtr theString) |
{ |
Rect itemRect; |
Handle itemHandle; |
short itemType; |
// Get handle to dialog item. |
GetDialogItem(theDialog, item, &itemType, &itemHandle, &itemRect); |
SetDialogItemText(itemHandle, theString); // Set the items text to theString. |
} |
//-------------------------------------------------------------- SetDialogNumToStr |
// This one is like SetDialogString() above, but it takes a number (long)É |
// instead of a string (the function will convert the long to a string for us). |
void SetDialogNumToStr (DialogPtr theDialog, short item, long theNumber) |
{ |
Str255 theString; |
Rect itemRect; |
Handle itemHandle; |
short itemType; |
NumToString(theNumber, theString); // Convert long to a string. |
GetDialogItem(theDialog, item, &itemType, &itemHandle, &itemRect); |
SetDialogItemText(itemHandle, theString); // Set the item's text to this number/string. |
} |
//-------------------------------------------------------------- GetDialogNumFromStr |
// This one is like GetDialogString() above, but returns a long (number)É |
// instead of a string (it does this by converting the string to a long). |
void GetDialogNumFromStr (DialogPtr theDialog, short item, long *theNumber) |
{ |
Str255 theString; |
Rect itemRect; |
Handle itemHandle; |
short itemType; |
// Get a handle to the dialog item. |
GetDialogItem(theDialog, item, &itemType, &itemHandle, &itemRect); |
GetDialogItemText(itemHandle, theString); // Get the item's text. |
StringToNum(theString, theNumber); // Convert the text to a long. |
} |
//-------------------------------------------------------------- DisableControl |
// Another dialog utility for "graying out" buttons or other controls in a dialog. |
void DisableControl (DialogPtr theDialog, short whichItem) |
{ |
Rect iRect; |
Handle iHandle; |
short iType; |
// Get a handle to the dialog item. |
GetDialogItem(theDialog, whichItem, &iType, &iHandle, &iRect); |
// Set it's "hilite state" to "grayed out". |
HiliteControl((ControlHandle)iHandle, kInactive); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-10-14