TweetFollow Us on Twitter

MACINTOSH C

Demonstration Program

Go to Contents
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// GWorldPicCursIcon.c
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// 
// This program:
//
// „  Opens a window in which the results of various drawing, copying, and cursor shape
//    change operations are  displayed.
//
// „  Demonstrates offscreen graphics world, picture, cursor, cursor shape change, 
//    animated cursor, and icon  operations as a result of the user choosing items from
//    a Demonstration menu.
//
// „  Demonstrates a modal dialog-based About... box containing a picture.
//
// To keep the non-demonstration code to a minimum, the program contains no functions 
// for updating the window or for responding to activate and operating system events.
//
// The program utilises the following resources:
//
// „  An 'MBAR' resource and associated 'MENU' resources (preload, non-purgeable).
//
// „  A 'WIND' resource (purgeable) (initially visible). 
//
// „  An 'acur' resource (purgeable).
//
// „  'CURS' resources associated with the 'acur' resource (preload, purgeable).
//
// „  Two 'cicn' resources (purgeable), one for the Icons menu item and one for drawing
//    in the window.
//
// „  Two icon family resources (purgeable), both for drawing in the window.
//
// „  A 'DLOG' resource (purgeable) and an associated 'DITL' resource (purgeable) and 
//    'PICT' resource for an About GWorldPicCursIcon. dialog box.
//
// „  A 'STR#' resource (purgeable) containing transform constants.
//
// „  A 'SIZE' resource with the acceptSuspendResumeEvents and is32BitCompatible flags 
//    set.
//
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

// ............................................................................. includes

#include <Appearance.h>
#include <Devices.h>
#include <Gestalt.h>
#include <PictUtils.h>
#include <Sound.h>
#include <ToolUtils.h>
#include <Resources.h>

// .............................................................................. defines

#define rMenubar                   128
#define rWindow                    128
#define mApple                     128
#define  iAbout                    1
#define mFile                      129
#define  iQuit                     11
#define  mDemonstration            131
#define  iOffScreenGWorld1         1
#define  iOffScreenGWorld2         2
#define  iPicture                  3
#define  iCursor                   4
#define  iAnimatedCursor           5
#define  iIcon                     6
#define rBeachBallCursor           128
#define rPicture                   128
#define rTransformStrings          128
#define rIconFamily1               128
#define rIconFamily2               129
#define rColourIcon                128
#define rAboutDialog               128
#define kSleepTime                 1
#define kBeachBallTickInterval     5
#define kCountingHandTickInterval  30

#define MAXLONG                    0x7FFFFFFF
#define topLeft(r)                 (((Point *) &(r))[0])
#define botRight(r)                (((Point *) &(r))[1])

// ............................................................................. typedefs

typedef struct
{
  SInt16      numberOfFrames;
  SInt16      whichFrame;
  CursHandle  frame[];
} animCurs, *animCursPtr, **animCursHandle;

// ..................................................................... global variables

Ptr              gPreAllocatedBlockPtr;
Boolean          gMacOS85Present       = false;
WindowPtr        gWindowPtr;
Boolean          gDone;
Boolean          gInBackground;
SInt32           gSleepTime;
RgnHandle        gCursorRegion;
Boolean          gCursorRegionsActive  = false;
Boolean          gAnimCursActive       = false;
Handle           gAnimCursResourceHdl  = NULL;    
animCursHandle   gAnimCursHdl;
SInt16           gAnimCursTickInterval;
SInt32           gAnimCursLastTick;
RGBColor         gBlackColour          = { 0x0000, 0x0000, 0x0000 };  
RGBColor         gWhiteColour          = { 0xFFFF, 0xFFFF, 0xFFFF };
RGBColor         gBeigeColour          = { 0xF000, 0xE300, 0xC200 };
RGBColor         gBlueColour           = { 0x4444, 0x4444, 0x9999 };

// .................................................................. function prototypes

void    main                   (void);
void    doInitManagers         (void);
void    eventLoop              (void);
void    doEvents               (EventRecord *);
void    doMenuChoice           (SInt32);
void    doOffScreenGWorld1     (void);
void    doOffScreenGWorld2     (void);
void    doPicture              (void);
void    doCursor               (void);
void    doChangeCursor         (WindowPtr,RgnHandle);
void    doAnimCursor           (void);
Boolean doGetAnimCursor        (SInt16,SInt16);
void    doIncrementAnimCursor  (void);
void    doReleaseAnimCursor    (void);
void    doIdle                 (void);
void    doIcon                 (void);
void    doAboutDialog          (void);
void    doGWorldDrawing        (void);
UInt16  doRandomNumber         (UInt16,UInt16);

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× main

void  main(void)
{
  OSErr       osError;
  SInt32      response;
  Handle      menubarHdl;
  MenuHandle  menuHdl;

  // ................... get nonrelocatable block low in heap for About. dialog structure

  if(!(gPreAllocatedBlockPtr = NewPtr(sizeof(DialogRecord))))
    ExitToShell();

  // ................................................................ initialise managers

  doInitManagers();

  // ....................................... check whether Mac OS 8.5 or later is present

  osError = Gestalt(gestaltSystemVersion,&response);
  
  if(osError == noErr && response >= 0x00000850)
    gMacOS85Present = true;

  // ........................................................ see random number generator
  
  GetDateTime((UInt32 *) (&qd.randSeed));

  // .......................................................... set up menu bar and menus
  
  if(!(menubarHdl = GetNewMBar(rMenubar)))
    ExitToShell();
  SetMenuBar(menubarHdl);
  DrawMenuBar();
  if(!(menuHdl = GetMenuHandle(mApple)))
    ExitToShell();
  else
    AppendResMenu(menuHdl,'DRVR');

  // ........................................................................ open window

  if(!(gWindowPtr = GetNewCWindow(rWindow,NULL,(WindowPtr)-1)))
    ExitToShell();

  SetPort(gWindowPtr);
  TextSize(10);

  // ................................................................... enter event loop

  eventLoop();
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doInitManagers

void  doInitManagers(void)
{
  MaxApplZone();
  MoreMasters();

  InitGraf(&qd.thePort);
  InitFonts();
  InitWindows();
  InitMenus();
  TEInit();
  InitDialogs(NULL);

  InitCursor();
  FlushEvents(everyEvent,0);

  RegisterAppearanceClient();
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× eventLoop

void  eventLoop(void)
{
  EventRecord  eventStructure;
  Boolean      gotEvent;

  gDone = false;
  gSleepTime = MAXLONG;
  gCursorRegion = NULL;

  while(!gDone)
  {
    gotEvent = WaitNextEvent(everyEvent,&eventStructure,gSleepTime,gCursorRegion);
    if(gotEvent)
      doEvents(&eventStructure);
    else
      doIdle();
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doEvents

void  doEvents(EventRecord *eventStrucPtr)
{    
  WindowPtr  windowPtr;
  SInt16     partCode;
  SInt8      charCode;

  switch(eventStrucPtr->what)
  {
    case mouseDown:
      partCode = FindWindow(eventStrucPtr->where,&windowPtr);
      switch(partCode)
      {
        case inMenuBar:
          doMenuChoice(MenuSelect(eventStrucPtr->where));
          break;

        case inContent:
          if(windowPtr != FrontWindow())
            SelectWindow(windowPtr);
          break;

        case inDrag:
          DragWindow(windowPtr,eventStrucPtr->where,&qd.screenBits.bounds);
          if(gCursorRegionsActive)
            doChangeCursor(windowPtr,gCursorRegion);
          break;
      }
      break;

    case keyDown:
    case autoKey:
      charCode = eventStrucPtr->message & charCodeMask;
      if((eventStrucPtr->modifiers & cmdKey) != 0)
        doMenuChoice(MenuEvent(eventStrucPtr));
      break;
    
    case updateEvt:
      
      BeginUpdate((WindowPtr) eventStrucPtr->message);
      EndUpdate((WindowPtr) eventStrucPtr->message);
      break;

    case osEvt:
      switch((eventStrucPtr->message >> 24) & 0x000000FF)
      {
        case suspendResumeMessage:
          if((eventStrucPtr->message & resumeFlag) == 1)
          {
            gInBackground = false;
#if TARGET_CPU_PPC
            if(gMacOS85Present)
              SetThemeCursor(kThemeArrowCursor);
            else
#endif
            SetCursor(&qd.arrow);
          }
          else
            gInBackground = true;
          break;
        
        case mouseMovedMessage:
          doChangeCursor(FrontWindow(),gCursorRegion);
          break;
      }
      break;
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doMenuChoice

void  doMenuChoice(SInt32 menuChoice)
{
  SInt16  menuID, menuItem;
  Str255  itemName;
  SInt16  daDriverRefNum;

  menuID = HiWord(menuChoice);
  menuItem = LoWord(menuChoice);

  if(menuID == 0)
    return;

  if(gAnimCursActive == true)
  {
    gAnimCursActive = false;
#if TARGET_CPU_PPC
    if(gMacOS85Present)
      SetThemeCursor(kThemeArrowCursor);
    else
    {
      SetCursor(&qd.arrow);
      doReleaseAnimCursor();
    }
#else
    SetCursor(&qd.arrow);
    doReleaseAnimCursor();
#endif
    gSleepTime = MAXLONG;
  }

  if(gCursorRegionsActive == true)
  {
    gCursorRegionsActive = false;
    DisposeRgn(gCursorRegion);
    gCursorRegion = NULL;
  }

  switch(menuID)
  {
    case mApple:
      if(menuItem == iAbout)
        doAboutDialog();
      else
      {
        GetMenuItemText(GetMenuHandle(mApple),menuItem,itemName);
        daDriverRefNum = OpenDeskAcc(itemName);
      }
      break;

    case mFile:
      if(menuItem == iQuit)
        gDone = true;
      break;

    case mDemonstration:
      switch(menuItem)
      {
        case iOffScreenGWorld1:
          doOffScreenGWorld1();
          break;      

        case iOffScreenGWorld2:
          doOffScreenGWorld2();
          break;

        case iPicture:
          doPicture();
          break;

        case iCursor:
          doCursor();
          break;

        case iAnimatedCursor:
          doAnimCursor();
          break;

        case iIcon:
          doIcon();
          break;
      }
      break;
  }

  HiliteMenu(0);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doOffScreenGWorld1

void  doOffScreenGWorld1(void)
{

  CGrafPtr       windowPortPtr;
  GDHandle       deviceHdl;
  QDErr          qdErr;
  GWorldPtr      gworldPortPtr;
  PixMapHandle   gworldPixMapHdl;
  Boolean        lockPixResult;
  Rect           sourceRect, destRect;  

  // ..................................................................... draw in window

  RGBBackColor(&gBlueColour);
  EraseRect(&gWindowPtr->portRect);

  SetWTitle(gWindowPtr,"\pSimulated time-consuming drawing operation");

  doGWorldDrawing();
  
  SetWTitle(gWindowPtr,"\pClick mouse to repeat in offscreen graphics port");

  while(!Button()) ;
  
  RGBBackColor(&gBlueColour);
  EraseRect(&gWindowPtr->portRect);
  RGBForeColor(&gWhiteColour);
  MoveTo(190,180);
  DrawString("\pPlease Wait.  Drawing in offscreen graphics port.");  

  // ................................. draw in offscreen graphics port and copy to window

  SetCursor(*(GetCursor(watchCursor)));

  // .................... save current graphics world and create offscreen graphics world

  GetGWorld(&windowPortPtr,&deviceHdl);

  qdErr = NewGWorld(&gworldPortPtr,0,&gWindowPtr->portRect,NULL,NULL,0);
  if(gworldPortPtr == NULL || qdErr != noErr)
  {
    SysBeep(10);
    return;
  }

  SetGWorld(gworldPortPtr,NULL);

  // .............. lock pixel image for duration of drawing and erase offscreen to white

  gworldPixMapHdl = GetGWorldPixMap(gworldPortPtr);

  if(!(lockPixResult = LockPixels(gworldPixMapHdl)))
  {
    SysBeep(10);
    return;
  }

  EraseRect(&(gworldPortPtr->portRect));  

  // .............................................. draw into the offscreen graphics port

  doGWorldDrawing();

  // ....................................................... restore saved graphics world

  SetGWorld(windowPortPtr,deviceHdl);

  // .............................................. set source and destination rectangles

  sourceRect = gworldPortPtr->portRect;
  destRect = windowPortPtr->portRect;

  // ........ ensure background colour is white and foreground colour in black, then copy

  RGBBackColor(&gWhiteColour);
  RGBForeColor(&gBlackColour);

  CopyBits(&((GrafPtr) gworldPortPtr)->portBits,
           &((GrafPtr) windowPortPtr)->portBits,
           &sourceRect,&destRect,srcCopy,NULL);

  if(QDError() != noErr)
    SysBeep(10);

  // ........................................................................... clean up 

  UnlockPixels(gworldPixMapHdl);
  DisposeGWorld(gworldPortPtr);  

  SetCursor(&qd.arrow);

  SetWTitle((WindowPtr) windowPortPtr,
            "\pOffscreen Graphics Worlds, Pictures,  Cursors and Icons");
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doOffScreenGWorld2

void  doOffScreenGWorld2(void)
{
  PicHandle      picture1Hdl,picture2Hdl;
  Rect           sourceRect, maskRect, maskDisplayRect, dest1Rect, dest2Rect, destRect;
  CGrafPtr       windowPortPtr;  
  GDHandle       deviceHdl;
  QDErr          qdErr;
  GWorldPtr      gworldPortPtr;
  PixMapHandle   gworldPixMapHdl;
  RgnHandle      region1Hdl, region2Hdl, regionHdl;
  SInt16         a, sourceMode;

  RGBBackColor(&gBeigeColour);
  EraseRect(&gWindowPtr->portRect);

  // ................................... get the source picture and draw it in the window

  if(!(picture1Hdl = GetPicture(rPicture)))
    ExitToShell();
  HNoPurge((Handle) picture1Hdl);
  SetRect(&sourceRect,116,35,273,147);
  DrawPicture(picture1Hdl,&sourceRect);
  HPurge((Handle) picture1Hdl);
  MoveTo(116,32);
  DrawString("\pSource image");
  
  // .................... save current graphics world and create offscreen graphics world

  GetGWorld(&windowPortPtr,&deviceHdl);  

  SetRect(&maskRect,0,0,157,112);

  qdErr = NewGWorld(&gworldPortPtr,0,&maskRect,NULL,NULL,0);  

  if(gworldPortPtr == NULL || qdErr != noErr)
  {
    SysBeep(10);
    return;
  }

  SetGWorld(gworldPortPtr,NULL);

  // .............. lock pixel image for duration of drawing and erase offscreen to white

  gworldPixMapHdl = GetGWorldPixMap(gworldPortPtr);

  if(!(LockPixels(gworldPixMapHdl)))
  {
    SysBeep(10);
    return;
  }

  EraseRect(&gworldPortPtr->portRect);  

  // ............................ get mask picture and draw it in offscreen graphics port

  if(!(picture2Hdl = GetPicture(rPicture + 1)))
    ExitToShell();
  HNoPurge((Handle) picture2Hdl);
  DrawPicture(picture2Hdl,&maskRect);

  // ......................................................... also draw it in the window

  SetGWorld(windowPortPtr,deviceHdl);
  SetRect(&maskDisplayRect,329,35,485,146);
  DrawPicture(picture2Hdl,&maskDisplayRect);
  HPurge((Handle) picture2Hdl);
  MoveTo(329,32);
  DrawString("\pCopy of offscreen mask");

  // ................... define an oval-shaped region and a round rectangle-shaped region
  
  SetRect(&dest1Rect,22,171,296,366);
  region1Hdl = NewRgn();
  OpenRgn();
  FrameOval(&dest1Rect);
  CloseRgn(region1Hdl);

  SetRect(&dest2Rect,308,171,582,366);
  region2Hdl = NewRgn();
  OpenRgn();
  FrameRoundRect(&dest2Rect,100,100);
  CloseRgn(region2Hdl);

  SetWTitle((WindowPtr) windowPortPtr,"\pClick mouse to copy");
  while(!Button()) ;

  // ... set background and foreground colour, then copy source to destination using mask 

  RGBForeColor(&gBlackColour);
  RGBBackColor(&gWhiteColour);

  for(a=0;a<2;a++)
  {
    if(a == 0)
    {
      regionHdl = region1Hdl;
      destRect = dest1Rect;
      sourceMode = srcCopy;
      MoveTo(22,168);
      DrawString("\pBoolean source mode srcCopy");
    }
    else
    {
      regionHdl = region2Hdl;
      destRect = dest2Rect;
      sourceMode = srcXor;
      MoveTo(308,168);
      DrawString("\pBoolean source mode srcXor");
    }

    CopyDeepMask(&((GrafPtr) windowPortPtr)->portBits,
                 &((GrafPtr) gworldPortPtr)->portBits,
                 &((GrafPtr) windowPortPtr)->portBits,
                 &sourceRect,&maskRect,&destRect,sourceMode + ditherCopy,regionHdl);

    if(QDError() != noErr)
      SysBeep(10);
  }

  // ........................................................................... clean up 

  UnlockPixels(gworldPixMapHdl);
  DisposeGWorld(gworldPortPtr);  

  ReleaseResource((Handle) picture1Hdl);
  ReleaseResource((Handle) picture2Hdl);
  DisposeRgn(region1Hdl);
  DisposeRgn(region2Hdl);

  SetWTitle((WindowPtr) windowPortPtr,
            "\pOffscreen Graphics Worlds, Pictures,  Cursors and Icons");
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doPicture

void  doPicture(void)
{
  Rect            pictureRect, theRect;  
  OpenCPicParams  picParams;
  RgnHandle       oldClipRgn;
  PicHandle       pictureHdl;
  SInt16          a, left, top, right, bottom, random;
  RGBColor        theColour;
  OSErr           osError;
  PictInfo        pictInfo;
  Str255          theString;

  RGBBackColor(&gWhiteColour);
  EraseRect(&gWindowPtr->portRect);

  // ........................................................... define picture rectangle

  pictureRect = gWindowPtr->portRect;
  pictureRect.right = (gWindowPtr->portRect.right - gWindowPtr->portRect.left) / 2;
  InsetRect(&pictureRect,10,10);

  // ................................................................ set clipping region
    
  oldClipRgn = NewRgn();
  GetClip(oldClipRgn);
  ClipRect(&pictureRect);

  // .................................................... set up OpenCPicParams structure 

  picParams.srcRect = pictureRect;
  picParams.hRes    = 0x00480000;
  picParams.vRes    = 0x00480000;
  picParams.version = -2;

  // ..................................................................... record picture

  pictureHdl = OpenCPicture(&picParams);

  RGBBackColor(&gBlueColour);
  EraseRect(&pictureRect);
  
  for(a=0;a<300;a++)
  {
    theRect = pictureRect;

    theColour.red   = doRandomNumber(0,65535);
    theColour.green = doRandomNumber(0,65535);
    theColour.blue  = doRandomNumber(0,65535);
    RGBForeColor(&theColour);

    left = doRandomNumber(10,(UInt16) theRect.right + 1);  
    top = doRandomNumber(10,(UInt16) theRect.bottom + 1);
    right = doRandomNumber((UInt16) left,(UInt16) theRect.right + 1);  
    bottom = doRandomNumber((UInt16) top,(UInt16)theRect.bottom + 1);    
    SetRect(&theRect,left,top,right,bottom);

    PenMode(doRandomNumber(addOver,adMin + 1));
    
    random = doRandomNumber(0,6);

    if(random == 0)
    {
      MoveTo(left,top);
      LineTo(right - 1,bottom - 1);
    }
    else if(random == 1)
      PaintRect(&theRect);
    else if(random == 2)
      PaintRoundRect(&theRect,30,30);
    else if(random == 3)
      PaintOval(&theRect);
    else if(random == 4)
      PaintArc(&theRect,0,300);
    else if(random == 5)
    {
      TextSize(doRandomNumber(10,70));
      MoveTo(left,right);
      DrawString("\pGWorldPicCursIcon");
    }
  }    

  // ........................ stop recording, draw picture, restore saved clipping region

  ClosePicture();

  DrawPicture(pictureHdl,&pictureRect);

  SetClip(oldClipRgn);
  DisposeRgn(oldClipRgn);

  // ............................... display some information from the PictInfo structure

  RGBForeColor(&gBlueColour);
  RGBBackColor(&gBeigeColour);
  PenMode(patCopy);
  OffsetRect(&pictureRect,300,0);
  EraseRect(&pictureRect);
  FrameRect(&pictureRect);
  TextSize(10);

  if(osError = GetPictInfo(pictureHdl,&pictInfo,recordFontInfo + returnColorTable,1,
                           systemMethod,0))
    SysBeep(10);

  MoveTo(380,70);
  DrawString("\pSome Picture Information:");

  MoveTo(380,100);
  DrawString("\pLines: ");
  NumToString(pictInfo.lineCount,theString);
  DrawString(theString);

  MoveTo(380,115);
  DrawString("\pRectangles: ");
  NumToString(pictInfo.rectCount,theString);
  DrawString(theString);

  MoveTo(380,130);
  DrawString("\pRound rectangles: ");
  NumToString(pictInfo.rRectCount,theString);
  DrawString(theString);

  MoveTo(380,145);
  DrawString("\pOvals: ");
  NumToString(pictInfo.ovalCount,theString);
  DrawString(theString);

  MoveTo(380,160);
  DrawString("\pArcs: ");
  NumToString(pictInfo.arcCount,theString);
  DrawString(theString);

  MoveTo(380,175);
  DrawString("\pPolygons: ");
  NumToString(pictInfo.polyCount,theString);
  DrawString(theString);

  MoveTo(380,190);
  DrawString("\pRegions: ");
  NumToString(pictInfo.regionCount,theString);
  DrawString(theString);

  MoveTo(380,205);
  DrawString("\pText strings: ");
  NumToString(pictInfo.textCount,theString);
  DrawString(theString);

  MoveTo(380,220);
  DrawString("\pUnique fonts: ");
  NumToString(pictInfo.uniqueFonts,theString);
  DrawString(theString);

  MoveTo(380,235);
  DrawString("\pUnique colours: ");
  NumToString(pictInfo.uniqueColors,theString);
  DrawString(theString);

  MoveTo(380,250);
  DrawString("\pFrame rectangle left: ");
  NumToString(pictInfo.sourceRect.left,theString);
  DrawString(theString);

  MoveTo(380,265);
  DrawString("\pFrame rectangle top: ");
  NumToString(pictInfo.sourceRect.top,theString);
  DrawString(theString);

  MoveTo(380,280);
  DrawString("\pFrame rectangle right: ");
  NumToString(pictInfo.sourceRect.right,theString);
  DrawString(theString);

  MoveTo(380,295);
  DrawString("\pFrame rectangle bottom: ");
  NumToString(pictInfo.sourceRect.bottom,theString);
  DrawString(theString);

  // ....................................... release memory occupied by Picture structure

  KillPicture(pictureHdl);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doCursor

void  doCursor(void)
{
  Rect    cursorRect;
  SInt16  a;

  RGBBackColor(&gBlueColour);
  EraseRect(&gWindowPtr->portRect);

  cursorRect = gWindowPtr->portRect;

  for(a=0;a<3;a++)
  {
    InsetRect(&cursorRect,40,40);

    if(a == 0 || a == 2)
      RGBBackColor(&gBeigeColour);
    else
      RGBBackColor(&gBlueColour);

    EraseRect(&cursorRect);
  }

  RGBForeColor(&gBeigeColour);
  MoveTo(10,20);
  DrawString("\pArrow cursor region");
  RGBForeColor(&gBlueColour);
  MoveTo(50,60);
  DrawString("\pIBeam cursor region");
  RGBForeColor(&gBeigeColour);
  MoveTo(90,100);
  DrawString("\pCross cursor region");
  RGBForeColor(&gBlueColour);
  MoveTo(130,140);
  DrawString("\pPlus cursor region");

  gCursorRegionsActive = true;
  gCursorRegion = NewRgn();
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doChangeCursor

void  doChangeCursor(WindowPtr windowPtr,RgnHandle cursorRegion)
{
  RgnHandle  arrowCursorRgn;
  RgnHandle  ibeamCursorRgn;
  RgnHandle  crossCursorRgn;
  RgnHandle  plusCursorRgn;
  Rect       cursorRect;
  GrafPtr    oldPort;  
  Point      mousePosition;

  arrowCursorRgn = NewRgn();
  ibeamCursorRgn = NewRgn();  
  crossCursorRgn = NewRgn();
  plusCursorRgn   = NewRgn();

  SetRectRgn(arrowCursorRgn,-32768,-32768,32766,32766);

  cursorRect = windowPtr->portRect;
  GetPort(&oldPort);
  SetPort(windowPtr);
  LocalToGlobal(&topLeft(cursorRect));
  LocalToGlobal(&botRight(cursorRect));  

  InsetRect(&cursorRect,40,40);
  RectRgn(ibeamCursorRgn,&cursorRect);
  DiffRgn(arrowCursorRgn,ibeamCursorRgn,arrowCursorRgn);

  InsetRect(&cursorRect,40,40);
  RectRgn(crossCursorRgn,&cursorRect);
  DiffRgn(ibeamCursorRgn,crossCursorRgn,ibeamCursorRgn);
  
  InsetRect(&cursorRect,40,40);
  RectRgn(plusCursorRgn,&cursorRect);
  DiffRgn(crossCursorRgn,plusCursorRgn,crossCursorRgn);

  GetMouse(&mousePosition);
  LocalToGlobal(&mousePosition);

  if(PtInRgn(mousePosition,ibeamCursorRgn))
  {
#if TARGET_CPU_PPC
    if(gMacOS85Present)
      SetThemeCursor(kThemeIBeamCursor);
    else
#endif
    SetCursor(*(GetCursor(iBeamCursor)));
    CopyRgn(ibeamCursorRgn,cursorRegion);
  }
  else if(PtInRgn(mousePosition,crossCursorRgn))
  {
#if TARGET_CPU_PPC
    if(gMacOS85Present)
      SetThemeCursor(kThemeCrossCursor);
    else
#endif
    SetCursor(*(GetCursor(crossCursor)));
    CopyRgn(crossCursorRgn,cursorRegion);
  }
  else if(PtInRgn(mousePosition,plusCursorRgn))
  {
#if TARGET_CPU_PPC
    if(gMacOS85Present)
      SetThemeCursor(kThemePlusCursor);
    else
#endif
    SetCursor(*(GetCursor(plusCursor)));
    CopyRgn(plusCursorRgn,cursorRegion);
  }
  else
  {
#if TARGET_CPU_PPC
    if(gMacOS85Present)
      SetThemeCursor(kThemeArrowCursor);
    else
#endif
    SetCursor(&qd.arrow);
    CopyRgn(arrowCursorRgn,cursorRegion);
  }

  DisposeRgn(arrowCursorRgn);
  DisposeRgn(ibeamCursorRgn);
  DisposeRgn(crossCursorRgn);
  DisposeRgn(plusCursorRgn);

  SetPort(oldPort);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doAnimCursor

void  doAnimCursor(void)
{
  SInt16  animCursResourceID, animCursTickInterval;

  BackColor(whiteColor);
  FillRect(&(gWindowPtr->portRect),&qd.white);

#if TARGET_CPU_PPC
  if(gMacOS85Present)
  {
    gAnimCursTickInterval = kCountingHandTickInterval;
    gSleepTime = gAnimCursTickInterval;
    gAnimCursActive = true;
  }
  else
  {
#endif
    animCursResourceID   = rBeachBallCursor;
    animCursTickInterval = kBeachBallTickInterval;

    if(doGetAnimCursor(animCursResourceID,animCursTickInterval))
    {
      gSleepTime = animCursTickInterval;
      gAnimCursActive = true;
    }  
    else
      SysBeep(10);
#if TARGET_CPU_PPC
  }
#endif
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doGetAnimCursor

Boolean  doGetAnimCursor(SInt16 resourceID,SInt16 tickInterval)
{
  SInt16   cursorID, a = 0;
  Boolean  noError = false;

  if((gAnimCursHdl = (animCursHandle) GetResource('acur',resourceID)))
  {
    noError = true;
    while((a < (*gAnimCursHdl)->numberOfFrames)  && noError)
    {
      cursorID = (SInt16) HiWord((SInt32) (*gAnimCursHdl)->frame[a]);
      (*gAnimCursHdl)->frame[a] = GetCursor(cursorID);
      if((*gAnimCursHdl)->frame[a])
        a++;
      else
        noError = false;
    }
  }

  if(noError)
  {
    gAnimCursTickInterval = tickInterval;
    gAnimCursLastTick = TickCount();
    (*gAnimCursHdl)->whichFrame = 0;
  }

  return(noError);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doIncrementAnimCursor

void  doIncrementAnimCursor(void)
{
  register  SInt32  newTick;
  static    UInt32  animationStep;
  
  newTick = TickCount();
  if(newTick < (gAnimCursLastTick + gAnimCursTickInterval))
    return;

#if TARGET_CPU_PPC
  if(gMacOS85Present)
  {
    SetAnimatedThemeCursor(kThemeCountingUpAndDownHandCursor,animationStep);
      animationStep++;
  }
  else
  {
#endif
    SetCursor(*((*gAnimCursHdl)->frame[(*gAnimCursHdl)->whichFrame++]));
    if((*gAnimCursHdl)->whichFrame == (*gAnimCursHdl)->numberOfFrames)
      (*gAnimCursHdl)->whichFrame = 0;
#if TARGET_CPU_PPC
  }
#endif

  gAnimCursLastTick = newTick;
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doReleaseAnimCursor

void  doReleaseAnimCursor(void)
{
  SInt16 a;

  for(a=0;a<(*gAnimCursHdl)->numberOfFrames;a++)
    ReleaseResource((Handle) (*gAnimCursHdl)->frame[a]);

  ReleaseResource((Handle) gAnimCursHdl);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doIdle

void  doIdle(void)
{
  if(gAnimCursActive == true)
    doIncrementAnimCursor();
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doIcon

void  doIcon(void)
{
  SInt16             a, b, stringIndex = 1;
  Rect               theRect;
  IconTransformType  transform = 0;
  Str255             theString;
  Handle             iconSuiteHdl;
  CIconHandle        ciconHdl;

  RGBForeColor(&gBlueColour);
  RGBBackColor(&gBeigeColour);
  EraseRect(&gWindowPtr->portRect);

  // ......................................................... PlotIconID with transforms

  MoveTo(50,28);
  DrawString("\pPlotIconID with transforms");

  for(a=50;a<471;a+=140)
  {   
    if(a == 190)
      transform = 16384;
    if(a == 330)
      transform = 256;
        
    for(b=0;b<4;b++)
    {
      if(a == 470 && b == 3)
        continue;

      GetIndString(theString,rTransformStrings,stringIndex++);
      MoveTo(a,b * 60 + 47);
      DrawString(theString);
      
      SetRect(&theRect,a,b * 60 + 50,a + 32,b * 60 + 82);
      PlotIconID(&theRect,0,transform,rIconFamily1);
      SetRect(&theRect,a + 40,b * 60 + 50,a + 56,b * 60 + 66);
      PlotIconID(&theRect,0,transform,rIconFamily1);
      SetRect(&theRect,a + 64,b * 60 + 50,a + 80,b * 60 + 62);
      PlotIconID(&theRect,0,transform,rIconFamily1);
    
      if(a >= 330)
        transform += 256;
      else
        transform ++;
    }
  }

  // ..................................................... GetIconSuite and PlotIconSuite

  MoveTo(50,275);
  LineTo(550,275);
  MoveTo(50,299);
  DrawString("\pGetIconSuite and PlotIconSuite");

  GetIconSuite(&iconSuiteHdl,rIconFamily2,kSelectorAllLargeData);

  SetRect(&theRect,50,324,82,356);
  PlotIconSuite(&theRect,kAlignNone,kTransformNone,iconSuiteHdl);
  SetRect(&theRect,118,316,166,364);
  PlotIconSuite(&theRect,kAlignNone,kTransformNone,iconSuiteHdl);
  SetRect(&theRect,202,308,266,372);
  PlotIconSuite(&theRect,kAlignNone,kTransformNone,iconSuiteHdl);

  // ............................................................. GetCIcon and PlotCIcon

  MoveTo(330,299);
  DrawString("\pGetCIcon and PlotCIcon");

  ciconHdl = GetCIcon(rColourIcon);

  SetRect(&theRect,330,324,362,356);
  PlotCIcon(&theRect,ciconHdl);
  SetRect(&theRect,398,316,446,364);
  PlotCIcon(&theRect,ciconHdl);
  SetRect(&theRect,482,308,546,372);
  PlotCIcon(&theRect,ciconHdl);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doAboutDialog

void  doAboutDialog(void)
{
  DialogPtr  dialogPtr;
  SInt16    itemHit;
    
  dialogPtr = GetNewDialog(rAboutDialog,gPreAllocatedBlockPtr,(WindowPtr)-1);
  ModalDialog(NULL,&itemHit);
  DisposeDialog(dialogPtr);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doGWorldDrawing

void  doGWorldDrawing(void)
{
  SInt32    a;
  SInt16    b, c;
  Rect      theRect;
  RGBColor  theColour;

  RGBForeColor(&gBeigeColour);
  PaintRect(&gWindowPtr->portRect);

  for(a=0;a<55000;a+=300)
  {
    theColour.red = a;
    theColour.blue = 55000 - a;
    theColour.green = 0;
    RGBForeColor(&theColour);

    for(b=10;b<531;b+=65)
    {
      for(c=10;c<331;c+=64)
      {
        SetRect(&theRect,b,c,b + 62,c + 61);
        PaintRect(&theRect);
      }
    }
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doRandomNumber

UInt16  doRandomNumber(UInt16 minimum, UInt16 maximum)
{
  UInt16  randomNumber;
  SInt32  range, t;

  randomNumber = Random();
  range = maximum - minimum;
  t = (randomNumber * range) / 65535;
  return(t + minimum);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

Demonstration Program Comments

If monitor colour depth is set to Thousands (or less), the Minimum Heap Size of 680K set
in the CodeWarrior project will be adequate.  If monitor depth is set to Millions, the
Minimum Heap Size will need to be increased.  The determinant is the memory required by
the offscreen graphics world created within the function doOffScreenGWorld1.

When this program is run, the user should:

*   Invoke the demonstrations by choosing items from the Demonstration menu, clicking the
    mouse when instructed to do so by the text in the window's title bar.

*   Click outside and inside the window when the cursor and animated cursor
    demonstrations have been invoked.

*   Choose the About... item in the Apple menu to display the About... dialog box.

*   Note that the Icons item in the Demonstration menu contains an icon.

When the PowerPC target is run, and Mac OS 8.5 or later is present, the new Appearance 
Manager Version 1.1 functions for setting cursor shape and animating the cursor are used.
When Mac OS 8.5 or later is not present (PowerPC target), or when the 68K target is run, 
the older cursor shape changing and animating functions and methodologies are used.

#define

In addition to the usual window and menu-related constants, constants are established for
the resource IDs of 'acur', 'PICT', 'STR#', icon family, and 'cicn' resources, and a
'DLOG' resource.  KSleeptime and MAXLONG will be assigned WaitNextEvent's sleep parameter
at various points in the program. kBeachBallTickInterval represents the interval between 
frame changes for the animated cursor (68K target, or PowerPC target when Mac OS 8.5 or 
later is not present).  kCountingHandTickInterval represents the interval between frame 
changes for the animated cursor (PowerPC target with Mac OS 8.5 or later present).

#typedef

The data type anumCurs is identical to the structure of an 'acur' resource.

Global Variables

gPreAllocatedBlock will be assigned a pointer to a nonrelocatable block for the 
dialog structure associated with the AboutÉ dialog.  gMacOS85Present will be assigned 
true if Mac OS 8.5 or later is present.  gWindowPtr will be assigned a pointer to the 
window's window structure.  gDone controls exit from the main event loop and thus 
program termination.  gInBackground relates to foreground/background switching.

In this program, the sleep and cursor region parameters in the WaitNextEvent call will be
changed during program execution, hence the global variables gSleepTime and
gCursorRegion.

gCursorRegion will be assigned a handle to a region which will passed in the mouseRgn
parameter of the WaitNextEvent call.  This relates to the cursor shape changing
demonstration.

gCursorRegionActive and gAnimCursActive will be set to true during, respectively, the
cursor and animated cursor demonstrations.  gAnimCursHdl will be assigned a handle to the
animCurs structure used during the animated cursor demonstration.  gAnimCursTickInterval
and gAnimCursLastTick also relate to the animated cursor demonstration.

gCursorRegionsActive will be set to true during the cursor shape changing demonstration. 
gAnimCursResourceHdl will be assigned a handle to the 'acur' structure associated with
the second of two animated cursors.  gAnimCurs1Active and gAnimCurs2Active will be set to
true during the animated cursor demonstrations.

main

The first action in the main function is to pre-allocate a nonrelocatable block for the
dialog structure for the About... modal dialog.  This is an anti-heap-fragmentation
measure.

If Mac OS 8.5 or later is present, gMacOS85Present is assigned true.

Random numbers will be used in the function doPicture.  The call to GetDateTime seeds the
random number generator.

Note that error handling here and in other areas of the program is somewhat rudimentary:
the program simply terminates, sometimes with a call to SysBeep(10).

eventLoop

eventLoop contains the main event loop.  The event loop terminates when gDone is set to
true.  Before the loop is entered, gSleepTime is set to MAXLONG. Initially, therefore,
the sleep parameter in the WaitNextEvent call is set to the maximum possible value.

The global variable passed in the mouseRgn parameter of the WaitNextEvent call is
assigned NULL so as to defeat the generation of mouse-moved events.

Each time round the loop, before the WaitNextEvent call, if the cursor shape changing
demonstration is under way (gCursorRegionActive = true) and the application is not in the
background, the application-defined function doChangeCursor is called.

If a null event is received, the application-defined function doIdle is called.  The
doIdle function has to do with the animated cursors demonstrations.

doEvents

In the inDrag case, after the call to DragWindow, and provided the cursor shape changing
demonstration is currently under way, the application-defined function doChangeCursor is
called. The regions controlling the generation of mouse-moved events are defined in
global coordinates, and are based on the window's port rectangle.  Accordingly, when the
window is moved, the new location of the port rectangle, in global coordinates, must be
re-calculated so that the various cursor regions may be re-defined.  The call to
doChangeCursor re-defines these regions for the new window location and copies the handle
to one of them, depending on the current location of the mouse cursor, to the global
variable gCursorRegion.  (Note that this call to doChangeCursor is also required, for the
same reason, when a window is re-sized or zoomed.)

In the case of a resume event:

„   If the target is the PowerPC target and Mac OS 8.5 or later is not present, or the
    target is the 68K target, SetCursor is called to ensure that the cursor is set to 
    the arrow shape.  The QuickDraw global variable arrow, which is of type Cursor, and
    which contains the arrow shaped cursor image, is passed in the newCursor parameter.

„   If the target is the PowerPC target and Mac OS 8.5 or later is present SetThemeCursor
    is called to ensure that the cursor is set to the appearance-compliant arrow shape.

In the case of a mouse-moved event (which occurs when the mouse cursor has moved outside
the region whose handle is currently being passed in WaitNextEvent's mouseRgn parameter),
doChangeCursor is called to change the handle passed in the mouseRgn parameter according
to the current location of the mouse.

doMenuChoice

doMenuChoice processes Apple and File menu choices to completion, with the exception of a
choice of the About... item in the Apple menu.  In this latter case, the
application-defined function doAboutDialog is called.  Choices from the Demonstration
menu result in calls to application-defined functions.

Before the main switch, however, certain actions relevant to the animated cursor and
cursor shape changing demonstrations are taken.

Firstly, if the animated cursor demonstration is currently under way, the flag which indicates 
this condition is set to false.  Then:

„   If the target is the PowerPC target and Mac OS 8.5 or later is not present, or the 
    target is the 68K target, SetCursor is called to set the cursor shape to the arrow 
    shape and memory associated with the animated cursor is released.

„    If the target is the PowerPC target and Mac OS 8.5 or later is present 
     SetThemeCursor is called to set the cursor to the appearance-compliant arrow shape.

WaitNextEvent's sleep parameter is then set to the maximum possible value.

Secondly, if the cursor shape changing demonstration is currently under way, the global
variable which signifies that situation is set to false.  In addition, the region
containing the current cursor region is disposed of and the associated global variable is
set to NULL, thus defeating the generation of mouse-moved events.

doOffScreenGWorld1

doWithoutOffScreenGWorld is the first demonstration.

Draw in Window

As a prelude for what is to come, the application-defined function doGWorldDrawing is
called to repeatedly paint some rectangles in the window in simulation of drawing
operations that take a short but nonetheless perceptible period of time to complete. 
This will be contrasted with the alternative of completing the drawing in an offscreen
graphics port and then copying it to the on-screen port.

Draw in Offscreen Graphics Port and Copy to Window

Firstly, the cursor is set the watch shape to indicate to the user that an operation
which will take but a second or two is taking place.

The call to GetGWorld saves the current graphics world, that is, the current colour
graphics port and the current device.

The call to NewGWorld creates an offscreen graphics world.  The offscreenGWorld parameter
receives a pointer to the offscreen graphics world's graphics port.  0 in the pixelDepth
means that the offscreen world's pixel depth will be set to the deepest device
intersecting the rectangle passed as the boundsRect parameter.  This Rect passed in the
boundsRect parameter becomes the offscreen port's portRect, the offscreen pixel map's
bounds and the offscreen device's gdRect.  NULL in the cTable parameter causes the
default colour table for the pixel depth to be used.  The aGDevice parameter is set to
NULL because the noNewDevice flag is not set.  0 in the flags parameter means that no
flags are set.

The call to SetGWorld sets the graphics port pointed to by gworldPortPtr as the current
graphics port. (When the first parameter is a GWorldPtr, the current device is set to the
device attached to the offscreen world and the second parameter is ignored.)

GetGWorldPixMap gets a handle to the offscreen pixel map and LockPixels called to prevent
the base address of the pixel image from being moved when the pixel image it is drawn
into or copied from.

The call to EraseRect clears the offscreen graphics port before the application-defined
function doGWorldDrawing is called to draw some graphics in the offscreen port.

With the drawing complete, the call to SetGWorld sets the (saved) window's colour
graphics port as the current port and the saved device as the current device.

The next two lines establish the source and destination rectangles (required by the
forthcoming call to CopyBits) as equivalent to the offscreen graphics world and window
port rectangles respectively.  The calls to RGBForeColor and RGBBackColor set the
foreground and background colours to black and white respectively, which is required to
ensure that the CopyBits call will produce predictable results in the colour sense.

The CopyBits call copies the image from the offscreen world to the window.  The call to
QDError checks for any error resulting from the last QuickDraw call (in this case,
CopyBits).

UnlockPixels unlocks the offscreen pixel image buffer and DisposeGWorld deallocates all
of the memory previously allocated for the offscreen graphics world.

Finally, SetCursor sets the cursor shape back to the standard arrow cursor.

DoOffScreenGWorld2

doWithoutOffScreenGWorld demonstrates the use of CopyDeepMask to copy a source pixel map
to a destination pixel map using a pixel map as a mask, and clipping the copying
operation to a designated region.  Because mask pixel maps cannot come from the screen,
an offscreen graphics world is created for the mask.

The first block loads a 'PICT' resource and draws the picture in the window.

The current graphics world is then saved and an offscreen graphics world the same size as
the drawn picture is created.  The offscreen graphics port is set as the current port,
the pixel map is locked, and the offscreen port is erased.

The second call to GetPicture loads the 'PICT' resource representing the mask and
DrawPicture is called to draw the mask in the offscreen port.

SetGWorld is then called again to make the window's colour graphics port the current
port.  The mask is then also drawn in the window next to the source image so that the
user can see a copy of the mask in the offscreen graphics port.

The next two blocks define two regions, one containing an oval and one a rounded
rectangle.  The handles to these regions will be passed in the maskRgn parameter of two
separate calls to CopyDeepMask.

Before the calls to CopyDeepMask, the foreground and background colours are set to black
and white respectively so that the results of the copying operation, in terms of colour,
will be predictable.

The for loop causes the source image to be copied to two locations in the window using a
different mask region and Boolean source mode for each copy.  The first time CopyDeepMask
is called, the oval-shaped region is passed in the maskRgn parameter and the source mode
srcCopy is passed in the mode parameter.  The second time CopyDeepMask is called, the
round rectangle-shaped region and srcOr and passed.

QDError checks for any error resulting from the last QuickDraw call (in this case,
CopyDeepMask).

In the clean-up, UnlockPixels unlocks the offscreen pixel image buffer, DisposeGWorld
deallocates all of the memory previously allocated for the offscreen graphics world, and
the memory occupied by the picture resources and regions is released.  Note that, because
the pictures are resources obtained via GetPicture, ReleaseResource, rather than
KillPicture, is used.

doPicture

doPicture demonstrates the recording and playing back a picture.
Define Picture Rectangle and Set Clipping Region

The window's port rectangle is copied to a local Rect variable.  This rectangle is then
made equal to the left half of the port rectangle, and then inset by 10 pixels all round. 
This is the picture rectangle

The clipping region is then set to be the equivalent of this rectangle.  (Before this
call, the clipping region is very large.  In fact, it is as large as the coordinate
plane.  If the clipping region is very large and you scale a picture while drawing it,
the clipping region can become invalid when DrawPicture scales the clipping region - in
which case the picture will not be drawn.)

Set up OpenCPicParams Structure

This block assigns values to the fields of an OpenCPicParams structure.  These specify
the previously defined rectangle as the bounding rectangle, and 72 pixels per inch
resolution both horizontally and vertically.  The version field should always be set to
-2.

Record Picture

OpenCPicture initiates the recording of the picture definition.  The address of the
OpenCPicParams structure is passed in the newHeader parameter.

The picture is then drawn.  Lines, rectangles, round rectangles, ovals, wedges, and text
are drawn in random colours, and sizes.

Stop Recording, Draw Picture, Restore Saved Clipping Region

The call to ClosePicture terminates picture recording and the call to DrawPicture draws
the picture by "playing back" the "recording" stored in the specified Picture structure.

The call to SetClip restores the saved clipping region and DisposeRgn frees the memory
associated with the saved region.

Display Some Information From The Pictinfo Structure

The call to GetPictInfo  returns information about the picture in a picture information
structure.  Information in some of the fields of this structure is then drawn in the
right side of the window.

Release Memory Occupied By Picture Structure

The call to KillPicture releases the memory occupied by the Picture structure.

doCursor

doCursor's chief purpose is to assign true to the global variable gCursorRegionsActive,
which will cause the application-defined function doChangeCursor to be called from within
main event loop provided the application is not in the background.  In addition, it
erases some rectangles in the window which visually represent to the user some cursor
regions which will later be established by the doChangeCursor function.

The last two lines sets the gCursorRegionsActive flag to true and create an empty region
for the last parameter of the WaitNextEvent call in the main event loop.  A handle to a
cursor region will be copied to gCursorRegion in the application-defined function
doChangeCursor.

doChangeCursor

doChangeCursor is called whenever a mouse-moved event is received and after the window is
dragged.

The first four lines create new empty regions to serve as the regions within which the
cursor shape will be changed to, respectively, the arrow, I-beam, cross, and plus shapes.

The SetRectRgn call sets the arrow cursor region to, initially, the boundaries of the
coordinate plane.  The next five lines establish a rectangle equivalent to the window's
port rectangle and change this rectangle's coordinates from local to global coordinates
so that the regions calculated from it will be in the required global coordinates.  The
call to InsetRect insets this rectangle by 40 pixels all round and the call to RectRgn
establishes this as the I-beam region.  The call to DiffRgn, in effect, cuts the
rectangle represented by the I-beam region from the arrow region, leaving a hollow arrow
region.

The next six lines use the same procedure to establish a rectangular hollow region for
the cross cursor and an interior rectangular region for the plus cursor.  The result of
all this is a rectangular plus cursor region in the centre of the window, surrounded by
(but not overlapped by) a hollow rectangular cross cursor region, this surrounded by (but
not overlapped by) a hollow rectangular I-beam cursor region, this surrounded by (but not
overlapped by) a hollow rectangular arrow cursor region the outside of which equates to
the boundaries of the coordinate plane.

The call to GetMouse gets the point representing the mouse's current position.  Since
GetMouse returns this point in local coordinates, the next line converts it to global
coordinates.

The next task is to determine the region in which the cursor is currently located.  The
calls to PtInRgn are made for that purpose.  Depending on which region is established as
the region in which the cursor in currently located, the cursor is set to the appropriate
shape and the handle to that region is copied to the global variable passed in
WaitNextEvent's mouseRgn parameter..  Note that:

„   If the target is the PowerPC target and Mac OS 8.5 or later is not present, or if 
    the target is the 68K target, SetCursor is used to change the cursor shape.

„   If the target is the PowerPC target and Mac OS 8.5 or later is present, 
    SetThemeCursor is used to change the appearance-compliant cursor shape.

That accomplished, the last four lines deallocate the memory associated with the regions
created earlier in the function.

doAnimCursor

doAnimCursor responds to the user's selection of the Animated Cursor item in the
Demonstration menu.

In this demonstration, application-defined functions are utilised to retrieve 'acur' and
'CURS' resources, spin the cursor, and deallocate the memory associated with the animated
cursor when the cursor is no longer required.  These functions are generic in that they
may be used to initialise, spin and release any animated cursor passed to the
getAnimCursor function as a formal parameter.  A "beach-ball" cursor is utilised in this
demonstration.  doAnimCursor's major role is simply to call getAnimCursor with the
beach-ball 'acur' resource as a parameter.

The fourth line assigns the resource ID of the beach-ball 'acur' resource to the variable
used as the first parameter in the later call to getAnimCursor call.  The fifth line
assigns a value represented by a constant to the second parameter in the getAnimCursor
call.  This value controls the frame rate of the cursor, that is, the number of ticks
which must elapse before the next frame (cursor) is displayed.  (The best frame rate
depends on the type of animated cursor used.)

If the call to getAnimCursor is successful, the flag gAnimCursActive is set to true and,
importantly, the sleep parameter in the WaitNextEvent call is set to the same ticks value
as that used to control the cursor's frame rate.  This latter will cause null events to
be generated at that tick interval (assuming, of course, that no other events intervene). 
Recall that the doIdle function is called whenever a null event is received and that, if
the flag gAnimCursActive is set to true, doIdle calls the spinCursor function.

If the call to getAnimCursor fails, doAnimCursor simply plays the system alert sound and
returns.

doGetAnimCursor

doGetAnimCursor retrieves the data in the specified 'acur' resource and stores it in
an animCurs structure, retrieves the 'CURS' resources specified in the 'acur' resource
and assigns the handles to the resulting Cursor structures to elements in an array in the
animCurs structure, establishes the frame rate for the cursor, and sets the starting
frame number.

GetResource is called to read the 'acur' resource into memory and return a handle to the
resource.  The handle is cast to type animCursHandle and assigned to the global variable
gAnimCursHdl (a handle to a structure of type animCurs, which is identical to the
structure of an 'acur' resource).  If this call is not successful (that is, GetResource
returns NULL), the function will simply exit, returning false to doAnimCursor.  If the
call is successful, noError is set to true before a while loop is entered.  This loop
will cycle once for each of the 'CURS' resources specified in the 'acur' resource -
assuming that noError is not set to false at some time during this process.

The ID of each cursor is stored in the high word of the specified element of the frame[]
field of the animCurs structure.  This is retrieved.  The cursor ID is then used in the
call to GetCursor to read in the resource (if necessary) and assign the handle to the
resulting 68-byte Cursor structure to the specified element of the frame[] field of the
animCurs structure.  If this pass through the loop was successful, the array index is
incremented; otherwise, noError is set to false, causing the loop and the function to
exit, returning false to doAnimCursor.

The first line within the if block assigns the ticks value passed to doGetAnimCursor to 
a global variable which will be utilised in the function doIncrementAnimCursor.  The 
next line assigns the number of ticks since system startup to another variable which 
will also be utilised in the function doIncrementAnimCursor.  The third line sets the 
starting frame number.

At this stage, the animated cursor has been initialised and doIdle will call 
doIncrementAnimCursor whenever null events are received.

doIncrementAnimCursor

doIncrementAnimCursor is called whenever null events are received.

The first line assigns the number of ticks since system startup to newTick.  The next 
line checks whether the specified number of ticks have elapsed since the previous call 
to doIncrementAnimCursor.  If the specified number of ticks have not elapsed, the 
function simply returns.  Otherwise, the following occurs:

„   If the target is the PowerPC target and Mac OS 8.5 or later is present, the new 
    Appearance Manager function SetThemeAnimatedCursor is called to increment the 
    appearance-compliant cursor frame.

„   If the target is the PowerPC target and Mac OS 8.5 or later is not present, or if 
    the target is the 68K target, SetCursor sets the cursor shape to that represented by 
    the handle stored in the specified element of the frame[] field of the animCurs 
    structure.   This line also increments the frame counter field (whichFrame) of the 
    animCurs  structure.  If whichFrame has been incremented to the last cursor in the 
    series, the  frame counter is re-set to 0.

The last line retrieves and stores the tick count at exit for use at the first line the 
next time the function is called.

doReleaseAnimCursor

doReleaseAnimCursor deallocates the memory occupied by the Cursor structures and the
'acur' resource.

Recall that doReleaseAnimCursor is called when the user clicks in the menu bar and that,
at the same time, the gAnimCursActive flag is set to false, the cursor is reset to the
standard arrow shape, and WaitNextEvent's sleep parameter is reset to the maximum
possible value.

doIdle

doIdle is called from the main event loop whenever a null event is received.  If the
active demonstration is the animated cursor demonstration, the application-defined
function doSpinCursor is called.
doIcon

doIcon demonstrates the drawing of icons in a window using PlotIconID, PoltIconSuite, and
PlotCIcon.

PlotIconID With Transforms

This block uses the function PlotIconID to draw an icon from an icon family with the
specified ID fifteen times, once for each of the fifteen available transform types. 
PlotIconID automatically chooses the appropriate icon resource from the icon suite
depending on the specified destination rectangle and the bit depth of the current device.

GetIconSuite and PlotIconSuite

This block uses GetIconSuite to get an icon suite comprising only the 'ICN#', 'icl4', and
'icl8' resources from the icon family with the specified resource ID.  PlotIconSuite is
then called three times to draw the appropriate icon within destination rectangles of
three different sizes.  PlotIconSuite automatically chooses the appropriate icon resource
from the icon suite depending on the specified destination rectangle and the bit depth of
the current device. PlotIconSuite also expands the icon to fit the last two destination
rectangles.

GetCIcon and PlotCIcon

This block uses GetCIcon to load the specified 'cicn' resource and PlotCIcon to draw the
colour icon within destination rectangles of three different sizes. PlotCIcon expands the
32 by 32 pixel icon to fit the last two destination rectangles.

doAboutDialog

doAboutDialog is called when the user chooses the About... item in the Apple menu.  

GetNewDialog creates a modal dialog. The dialog's item list contains a picture item,
which fills the entire dialog window.  Note that a pointer to a pre-allocated
nonrelocatable memory block is passed in the dStorage parameter

The call to ModalDialog means that the dialog will remain displayed until the user clicks
somewhere within the dialog box, at which time DisposeDialog is called to dismiss the
dialog and free the associated memory.  A dialog box rather than an alert box is used to
obviate the need for a button for dismissing the dialog.

doGWorldDrawing and doRandomNumber

doGWorldDrawing and doRandomNumber are both incidental to the demonstration,
doGWorldDrawing is called from doOffScreenGWorld1 to execute a drawing operation which
will take a short but nonetheless perceptible period of time.  doRandomNumber generates
the random numbers used within the doPicture function.
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, donā€™t remind me I could currently buy him this month Iā€™m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly ā€œborrowedā€ many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Marvel Future Fight celebrates nine year...
Announced alongside an advertising image I can only assume was aimed squarely at myself with the prominent Deadpool and Odin featured on it, Netmarble has revealed their celebrations for the 9th anniversary of Marvel Future Fight. The Countdown... | Read more »
HoYoFair 2024 prepares to showcase over...
To say Genshin Impact took the world by storm when it was released would be an understatement. However, I think the most surprising part of the launch was just how much further it went than gaming. There have been concerts, art shows, massive... | Read more »
Explore some of BBCs' most iconic s...
Despite your personal opinion on the BBC at a managerial level, it is undeniable that it has overseen some fantastic British shows in the past, and now thanks to a partnership with Roblox, players will be able to interact with some of these... | Read more »

Price Scanner via MacPrices.net

24-inch M1 iMacs available at Apple starting...
Apple has clearance M1 iMacs available in their Certified Refurbished store starting at $1049 and ranging up to $300 off original MSRP. Each iMac is in like-new condition and comes with Appleā€™s... Read more
Walmart continues to offer $699 13-inch M1 Ma...
Walmart continues to offer new Apple 13ā€³ M1 MacBook Airs (8GB RAM, 256GB SSD) online for $699, $300 off original MSRP, in Space Gray, Silver, and Gold colors. These are new MacBook for sale by... Read more
B&H has 13-inch M2 MacBook Airs with 16GB...
B&H Photo has 13ā€³ MacBook Airs with M2 CPUs, 16GB of memory, and 256GB of storage in stock and on sale for $1099, $100 off Appleā€™s MSRP for this configuration. Free 1-2 day delivery is available... Read more
14-inch M3 MacBook Pro with 16GB of RAM avail...
Apple has the 14ā€³ M3 MacBook Pro with 16GB of RAM and 1TB of storage, Certified Refurbished, available for $300 off MSRP. Each MacBook Pro features a new outer case, shipping is free, and an Apple 1-... Read more
Apple M2 Mac minis on sale for up to $150 off...
Amazon has Appleā€™s M2-powered Mac minis in stock and on sale for $100-$150 off MSRP, each including free delivery: ā€“ Mac mini M2/256GB SSD: $499, save $100 ā€“ Mac mini M2/512GB SSD: $699, save $100 ā€“... Read more
Amazon is offering a $200 discount on 14-inch...
Amazon has 14-inch M3 MacBook Pros in stock and on sale for $200 off MSRP. Shipping is free. Note that Amazonā€™s stock tends to come and go: ā€“ 14ā€³ M3 MacBook Pro (8GB RAM/512GB SSD): $1399.99, $200... Read more
Sunday Sale: 13-inch M3 MacBook Air for $999,...
Several Apple retailers have the new 13ā€³ MacBook Air with an M3 CPU in stock and on sale today for only $999 in Midnight. These are the lowest prices currently available for new 13ā€³ M3 MacBook Airs... Read more
Multiple Apple retailers are offering 13-inch...
Several Apple retailers have 13ā€³ MacBook Airs with M2 CPUs in stock and on sale this weekend starting at only $849 in Space Gray, Silver, Starlight, and Midnight colors. These are the lowest prices... Read more
Roundup of Verizonā€™s April Apple iPhone Promo...
Verizon is offering a number of iPhone deals for the month of April. Switch, and open a new of service, and you can qualify for a free iPhone 15 or heavy monthly discounts on other models: ā€“ 128GB... Read more
B&H has 16-inch MacBook Pros on sale for...
Apple 16ā€³ MacBook Pros with M3 Pro and M3 Max CPUs are in stock and on sale today for $200-$300 off MSRP at B&H Photo. Their prices are among the lowest currently available for these models. B... Read more

Jobs Board

IN6728 Optometrist- *Apple* Valley, CA- Tar...
Date: Apr 9, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92308 **Requisition ID:** 824398 At Target Optical, we help people see and look great - and Read more
Medical Assistant - Orthopedics *Apple* Hil...
Medical Assistant - Orthopedics Apple Hill York Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
*Apple* Systems Administrator - JAMF - Activ...
ā€¦**Public Trust/Other Required:** None **Job Family:** Systems Administration **Skills:** Apple Platforms,Computer Servers,Jamf Pro **Experience:** 3 + years of Read more
Liquor Stock Clerk - S. *Apple* St. - Idaho...
Liquor Stock Clerk - S. Apple St. Boise Posting Begin Date: 2023/10/10 Posting End Date: 2024/10/14 Category: Retail Sub Category: Customer Service Work Type: Part Read more
Top Secret *Apple* System Admin - Insight G...
Job Description Day to Day: * Configure and maintain the client's Apple Device Management (ADM) solution. The current solution is JAMF supporting 250-500 end points, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.