//============================================================================ //---------------------------------------------------------------------------- // Utilities.c //---------------------------------------------------------------------------- //============================================================================ #include "PLEventQueue.h" #include "PLKeyEncoding.h" #include "PLQDraw.h" #include "PLPasStr.h" #include "PLResources.h" #include "PLSound.h" #include "PLTimeTaggedVOSEvent.h" #include "QDPixMap.h" #include "Externs.h" #include "IconLoader.h" #include "InputManager.h" #include "ResourceManager.h" #include "Utilities.h" UInt32 theSeed; extern Boolean switchedOut; //-------------------------------------------------------------- ToolBoxInit // The standard ToolBox intialization that must happen when any MacÉ // program first launches. void ToolBoxInit (void) { InitCursor(); switchedOut = false; } //-------------------------------------------------------------- RandomInt // Returns a random integer (short) within "range". short RandomInt (short range) { register long rawResult; rawResult = Random(); if (rawResult < 0L) rawResult *= -1L; rawResult = (rawResult * (long)range) / 32768L; return ((short)rawResult); } //-------------------------------------------------------------- RandomLong // Returns a random long interger within "range". long RandomLong (long range) { register long highWord, lowWord; register long rawResultHi, rawResultLo; highWord = (range & 0xFFFF0000) >> 16; lowWord = range & 0x0000FFFF; rawResultHi = Random(); if (rawResultHi < 0L) rawResultHi *= -1L; rawResultHi = (rawResultHi * highWord) / 32768L; rawResultLo = Random(); if (rawResultLo < 0L) rawResultLo *= -1L; rawResultLo = (rawResultLo * lowWord) / 32768L; rawResultHi = (rawResultHi << 16) + rawResultLo; return (rawResultHi); } //-------------------------------------------------------------- RedAlert // Called when we must quit app. Brings up a dialog informing userÉ // of the problem and the exits to shell. void RedAlert (short errorNumber) { #define rDeathAlertID 170 // alert res. ID for death error #define rErrTitleID 170 // string ID for death error title #define rErrMssgID 171 // string ID for death error message short dummyInt; Str255 errTitle, errMessage, errNumberString; InitCursor(); if (errorNumber > 1) // <= 0 is unaccounted for { GetIndString(errTitle, rErrTitleID, errorNumber); GetIndString(errMessage, rErrMssgID, errorNumber); } else { GetIndString(errTitle, rErrTitleID, 1); GetIndString(errMessage, rErrMssgID, 1); } NumToString((long)errorNumber, errNumberString); ParamText(errTitle, errMessage, errNumberString, PSTR("")); // CenterAlert(rDeathAlertID); dummyInt = Alert(rDeathAlertID, nil); ExitToShell(); } //-------------------------------------------------------------- CreateOffScreenBitMap // Creates an offscreen bit map (b&w - 1 bit depth). /* 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 == nil) RedAlert(kErrNoMemory); theBitMap.bounds = *theRect; if (MemError() != PLErrors::kNone) RedAlert(kErrNoMemory); SetPortBits(&theBitMap); ClipRect(theRect); RectRgn(theBWPort->visRgn, theRect); EraseRect(theRect); *offScreen = theBWPort; } */ //-------------------------------------------------------------- CreateOffScreenPixMap // Creates an offscreen pix map using the depth of the current device. /* void CreateOffScreenPixMap (Rect *theRect, CGrafPtr *offScreen) { CTabHandle thisColorTable; GDHandle oldDevice; CGrafPtr newCGrafPtr; Ptr theseBits; long sizeOfOff, offRowBytes; PLError_t theErr; short thisDepth; char wasState; oldDevice = GetGDevice(); SetGDevice(thisGDevice); newCGrafPtr = nil; newCGrafPtr = (CGrafPtr)NewPtr(sizeof(CGrafPort)); if (newCGrafPtr != nil) { OpenCPort(newCGrafPtr); thisDepth = (**(*newCGrafPtr).portPixMap).pixelSize; offRowBytes = ((((long)thisDepth * (long)(theRect->right - theRect->left)) + 15L) >> 4L) << 1L; sizeOfOff = (long)(theRect->bottom - theRect->top + 1) * offRowBytes; // sizeOfOff = (long)(theRect->bottom - theRect->top) * offRowBytes; OffsetRect(theRect, -theRect->left, -theRect->top); theseBits = NewPtr(sizeOfOff); if (theseBits != nil) { // workaround (**(*newCGrafPtr).portPixMap).baseAddr = theseBits + offRowBytes; // (**(*newCGrafPtr).portPixMap).baseAddr = theseBits; (**(*newCGrafPtr).portPixMap).rowBytes = (short)offRowBytes + 0x8000; (**(*newCGrafPtr).portPixMap).bounds = *theRect; wasState = HGetState((Handle)thisGDevice); HLock((Handle)thisGDevice); thisColorTable = (**(**thisGDevice).gdPMap).pmTable; HSetState((Handle)thisGDevice, wasState); 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 = nil; RedAlert(kErrNoMemory); } } else RedAlert(kErrNoMemory); *offScreen = newCGrafPtr; SetGDevice(oldDevice); } */ //-------------------------------------------------------------------- CreateOffScreenGWorld // Creates an offscreen GWorldÊusing the depth passed in. PLError_t CreateOffScreenGWorld (DrawSurface **theGWorld, Rect *bounds, GpPixelFormat_t pixelFormat) { PLError_t theErr; theErr = NewGWorld(theGWorld, pixelFormat, bounds, nil); return theErr; } //-------------------------------------------------------------- KillOffScreenPixMap // Destroys memory allocated by an offscreen pix map. /* void KillOffScreenPixMap (CGrafPtr offScreen) { Ptr theseBits; if (offScreen != nil) { theseBits = (**(*offScreen).portPixMap).baseAddr; theseBits -= (**(*offScreen).portPixMap).rowBytes & 0x7FFF; // workaround DisposePtr(theseBits); DisposeHandle((Handle)(**(*offScreen).portPixMap).pmTable); CloseCPort(offScreen); DisposePtr((Ptr)offScreen); } } */ //-------------------------------------------------------------- KillOffScreenBitMap // Destroys memory allocated by an offscreen bit map. /* void KillOffScreenBitMap (GrafPtr offScreen) { if (offScreen != nil) { DisposePtr((Ptr)(offScreen->portBits.baseAddr)); ClosePort(offScreen); DisposePtr((Ptr)offScreen); } } */ //-------------------------------------------------------------- LoadGraphic // Function loads the specified 'PICT' from disk and draws it toÉ // the current port (no scaling, clipping, etc, are done). AlwaysÉ // draws in the upper left corner of current port. void LoadGraphic (DrawSurface *surface, short resID) { Rect bounds; THandle thePicture; thePicture = GetPicture(resID); if (thePicture == nil) RedAlert(kErrFailedGraphicLoad); bounds = (*thePicture)->picFrame.ToRect(); OffsetRect(&bounds, -bounds.left, -bounds.top); surface->DrawPicture(thePicture, bounds); thePicture.Dispose(); } //-------------------------------------------------------------- LoadScaledGraphic // Loads the specified 'PICT' and draws it mapped to the rectangleÉ // specified. If this rect isn't the same size of the 'PICT', scalingÉ // will occur. void LoadScaledGraphic (DrawSurface *surface, short resID, Rect *theRect) { THandle thePicture; thePicture = GetPicture(resID); if (thePicture == nil) RedAlert(kErrFailedGraphicLoad); surface->DrawPicture(thePicture, *theRect); thePicture.Dispose(); } //-------------------------------------------------------------- LargeIconPlot // Draws a standard b&w icon (32 x 32) - resource is an 'ICON'. bool LargeIconPlot (DrawSurface *surface, PortabilityLayer::ResourceArchive *resFile, short resID, const Rect &theRect) { Handle hdl = resFile->GetResource('icl8', resID, true); if (hdl) { THandle img = PortabilityLayer::IconLoader::GetInstance()->LoadSimpleColorIcon(hdl); if (img) { CopyBits(*img, *surface->m_port.GetPixMap(), &(*img)->m_rect, &theRect, srcCopy); img.Dispose(); } hdl.Dispose(); return true; } hdl = resFile->GetResource('ICN#', resID, true); if (hdl) { THandle img = PortabilityLayer::IconLoader::GetInstance()->LoadBWIcon(hdl); if (img) { CopyBits(*img, *surface->m_port.GetPixMap(), &(*img)->m_rect, &theRect, srcCopy); img.Dispose(); } hdl.Dispose(); return true; } return false; } //-------------------------------------------------------------- DrawCIcon // Draws a standard color icon (32 x 32) - resource is a 'CICN'. void DrawCIcon (DrawSurface *surface, short theID, short h, short v) { THandle colorImage; THandle bwImage; THandle maskImage; if (PortabilityLayer::IconLoader::GetInstance()->LoadColorIcon(theID, colorImage, bwImage, maskImage)) { Rect theRect; SetRect(&theRect, 0, 0, 32, 32); OffsetRect(&theRect, h, v); CopyMask(*colorImage, *maskImage, *surface->m_port.GetPixMap(), &(*colorImage)->m_rect, &(*maskImage)->m_rect, &theRect); surface->m_port.SetDirty(PortabilityLayer::QDPortDirtyFlag_Contents); bwImage.Dispose(); colorImage.Dispose(); maskImage.Dispose(); } } //-------------------------------------------------------------- LongSquareRoot // This is a quick and dirty square root function that returns prettyÉ // accurate long integer results. It uses no transcendental functions orÉ // floating point. long LongSquareRoot (long theNumber) { long currentAnswer; long nextTrial; if (theNumber <= 1L) return (theNumber); nextTrial = theNumber / 2; do { currentAnswer = nextTrial; nextTrial = (nextTrial + theNumber / nextTrial) / 2; } while (nextTrial < currentAnswer); return(currentAnswer); } //-------------------------------------------------------------- WaitForInputEvent // Wait for either a key to be hit or the mouse button to be clicked. // Also has a "timeout" parameter ("seconds"). Boolean WaitForInputEvent (short seconds) { TimeTaggedVOSEvent theEvent; long timeToBail; Boolean waiting, didResume; timeToBail = TickCount() + 60L * (long)seconds; FlushEvents(everyEvent, 0); waiting = true; didResume = false; while (waiting) { const KeyDownStates *theKeys = PortabilityLayer::InputManager::GetInstance()->GetKeys(); if (theKeys->IsSet(PL_KEY_EITHER_SPECIAL(kControl)) || theKeys->IsSet(PL_KEY_EITHER_SPECIAL(kAlt)) || theKeys->IsSet(PL_KEY_EITHER_SPECIAL(kShift))) waiting = false; if (PortabilityLayer::EventQueue::GetInstance()->Dequeue(&theEvent)) { if (theEvent.IsLMouseDownEvent() || theEvent.IsKeyDownEvent()) waiting = false; } if ((seconds != -1) && (TickCount() >= timeToBail)) waiting = false; Delay(1, nullptr); } FlushEvents(everyEvent, 0); return (didResume); } //-------------------------------------------------------------- WaitCommandQReleased // Waits until the Command-Q key combination is released. void WaitCommandQReleased (void) { Boolean waiting; waiting = true; while (waiting) { const KeyDownStates *theKeys = PortabilityLayer::InputManager::GetInstance()->GetKeys(); if (!theKeys->IsSet(PL_KEY_EITHER_SPECIAL(kControl)) || !theKeys->IsSet(PL_KEY_ASCII('Q'))) waiting = false; Delay(1, nullptr); } FlushEvents(everyEvent, 0); } //-------------------------------------------------------------- KeyMapOffsetFromRawKey // Converts a raw key code to keymap offset (ugly stuff). char KeyMapOffsetFromRawKey (char rawKeyCode) { char hiByte, loByte, theOffset; hiByte = rawKeyCode & 0xF0; loByte = rawKeyCode & 0x0F; if (loByte <= 0x07) theOffset = hiByte + (0x07 - loByte); else theOffset = hiByte + (0x17 - loByte); return (theOffset); } //-------------------------------------------------------------- GetKeyName // Given a keyDown event (it's message field), this function returnsÉ // a string with that key's name (so we get "Shift" and "Esc", etc.). void GetKeyName (intptr_t message, StringPtr theName) { PasStringCopy(PSTR("TODO"), theName); PL_NotYetImplemented_TODO("KeyNames"); } //-------------------------------------------------------------- OptionKeyDown // Returns true is the Option key is being held down. Boolean OptionKeyDown (void) { const KeyDownStates *theKeys = PortabilityLayer::InputManager::GetInstance()->GetKeys(); if (theKeys->IsSet(PL_KEY_EITHER_SPECIAL(kAlt))) return (true); else return (false); } //-------------------------------------------------------------- ExtractCTSeed // Very esoteric - gets the "color table seed" from a specified graf port. /* long ExtractCTSeed (CGrafPtr porter) { long theSeed; theSeed = (**((**(porter->portPixMap)).pmTable)).ctSeed; return(theSeed); } */ //-------------------------------------------------------------- ForceCTSeed // Forces the "color table seed" from a specified graf port to aÉ // specified value. /* void ForceCTSeed (CGrafPtr porter, long newSeed) { (**((**(porter->portPixMap)).pmTable)).ctSeed = newSeed; } */ //-------------------------------------------------------------- DelayTicks // Lil' function that just sits and waits a specified number ofÉ // Ticks (1/60 of a second). void DelayTicks (long howLong) { UInt32 whoCares; Delay(howLong, &whoCares); } //-------------------------------------------------------------- UnivGetSoundVolume // Returns the speaker volume (as set by the user) in the range ofÉ // zero to seven (handles Sound Manager 3 case as well). void UnivGetSoundVolume (short *volume, Boolean hasSM3) { long longVol; PLError_t theErr; // if (hasSM3) // { longVol = PortabilityLayer::SoundSystem::GetInstance()->GetVolume(); *volume = longVol / 0x0024; // } // else // GetSoundVol(volume); if (*volume > 7) *volume = 7; else if (*volume < 0) *volume = 0; } //-------------------------------------------------------------- UnivSetSoundVolume // Sets the speaker volume to a specified value (in the range ofÉ // zero to seven (handles Sound Manager 3 case as well). void UnivSetSoundVolume (short volume, Boolean hasSM3) { long longVol; if (volume > 7) volume = 7; else if (volume < 0) volume = 0; // if (hasSM3) // { longVol = (long)volume * 0x0025; if (longVol > 0xff) longVol = 0xff; PortabilityLayer::SoundSystem::GetInstance()->SetVolume(longVol); // } // else // SetSoundVol(volume); }