TweetFollow Us on Twitter

MACINTOSH C

Demonstration Program

Go to Contents
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// Miscellany.h
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// 
// This program demonstrates:
//
// Ą  The use of the Notification Manager to allow an application running in the 
//    background to communicate with the foreground application.
//
// Ą  The use of the determinate progress indicator control to show progress during a
//    time-consuming operation, together with scanning the event queue for Command-period
//    key-down events for the purpose of terminating the lengthy operation before it 
//    concludes of its own accord.
//
// Ą  Image drawing optimisation and window zooming in a multi-monitors environment. 
//
// Ą  The use of the Color Picker to solicit a choice of colour from the user.
//
// Ą  Slot-based VBL tasks.

// Ą  The use of stubs in 68K code segments, together with a function which uses those 
//    stubs  to unlock code segments and make them purgeable. 
//
// The program utilises the following resources:
//
// Ą  An 'MBAR' resource, and 'MENU' resources for Apple, File, Edit and Demonstration
//    menus (preload, non-purgeable).
//
// Ą  A 'WIND' resource (purgeable) (initially visible) for a window in which graphics
//    and information relevant to the demonstrations is displayed.
//
// Ą  A 'DLOG' resource (purgeable), and associated 'DITL', 'dlgx', and 'dftb' resources
//    (purgeable), for a dialog box in which the progress indicator is displayed.
//
// Ą  'CNTL' resources (purgeable) for the progress indicator dialog.
//
// Ą  'icn#', 'ics4', and 'ics8' resources (non-purgeable) which contain the application
//    icon shown in the Application menu during the Notification Manager demonstration.
//
// Ą  A 'snd ' resource (non-purgeable) used in the Notification Manager demonstration.
//
// Ą  A 'STR ' resource (non-purgeable) containing the text displayed in the alert box
//    invoked by the Notification Manager.
//
// Ą  A 'STR#' resource (purgeable) containing the label and narrative strings for the
//    notification-related alert displayed by Miscellany.
//
// Ą  A 'PICT' resource (non-purgeable) used in the slot-based VBL task demonstration. 
//
// Ą  A 'SIZE' resource with the acceptSuspendResumeEvents, doesActivateOnFGSwitch,
//    canBackgound, and is32BitCompatible flags set.
//
// Miscellany source code is contained in six files.  In the 68K project, each source 
// code file is assigned to a different segment.  (Note that this small program does not
// really require such segmentation.  The code is segmented only to facilitate the
// demonstration of the Segment Loader aspects, which apply only to the 68K version.)
//
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

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

#include <Appearance.h>
#include <ColorPicker.h>
#include <ControlDefinitions.h>
#include <Devices.h>
#include <LowMem.h>
#include <Retrace.h>
#include <SegLoad.h>
#include <Sound.h>
#include <ToolUtils.h>

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

#define mApple              128
#define  iAbout             1
#define mFile               129
#define  iQuit              11
#define mDemonstration      131
#define  iNotification      1
#define  iProgress          2
#define  iColourPicker      3
#define  iMultiMonitors     4
#define  iSlotVertBlank     5
#define rMenubar            128
#define rWindow             128
#define rAlert              128
#define rDialog             128
#define  iProgressIndicator 1
#define rIconFamily         128
#define rBarkSound          8192
#define rString             128
#define rAlertStrings       128
#define  indexLabel         1
#define  indexNarrative     2
#define rPicture            128
#define topLeft(r)          (((Point *) &(r))[0])
#define botRight(r)         (((Point *) &(r))[1])

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

typedef struct
{
  VBLTask   vblTaskStruc;
  SInt32    thisApplicationsA5;
  Boolean   inVBlankPeriod;
} VBLStructure, *VBLStructurePtr;

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

void        main                            (void);
void        doInitManagers                  (void);
void        doEvents                        (EventRecord *);
void        doMenuChoice                    (SInt32 menuChoice);
void        unloadSegments                  (void);

void        notificationSegment             (void);
void        doSetUpNotification             (void);
void        doPrepareNotificationStructure  (void);
void        doIdle                          (void);
void        doOSEvent                       (EventRecord *);
void        doDisplayMessageToUser          (void);

void        progressBarSegment              (void);
void        doProgressIndicator             (void);
Boolean     doCheckForCancel                (DialogPtr);

void        multiMonitorSegment             (void);
pascal void doDeviceLoopDraw                (SInt16,SInt16,GDHandle,SInt32);
void        doZoomWindowMultiMonitors       (WindowPtr,SInt16);
void        doRedoWindowContent             (WindowPtr);

void        colourPickerSegment             (void);
void        doColourPicker                  (void);
void        doDrawColourPickerChoice        (void);
char        *doDecimalToHexadecimal         (UInt16 n);

void        doStopSystemVBLTask             (void);
void        doSlotVBLTask                   (void);
OSErr        doInstallSlotVBLTask           (void);
#if TARGET_CPU_68K
void        theSlotVBLTask                  (void);
#else
void        theSlotVBLTask                  (VBLStructurePtr);
#endif
void        doStopSlotVBLTask               (void);

// ........................................................... in-line glue for GetVBLRec

#if TARGET_CPU_68K
pascal SInt32  GetVBLRec (void) = 0x2E88;
#endif

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// Miscellany.c
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

#include "Miscellany.h"

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

DeviceLoopDrawingUPP  doDeviceLoopDrawUPP;
Boolean               gDone;
WindowPtr             gWindowPtr;
ProcessSerialNumber   gProcessSerNum;
Rect                  gMultiMonDragBounds, gMultiMonGrowBounds;
Boolean               gMultiMonitorsDrawDemo = false;
Boolean               gColourPickerDemo  = false;
RGBColor              gWhiteColour = { 0xFFFF, 0xFFFF, 0xFFFF };
RGBColor              gBlueColour  = { 0x4444, 0x4444, 0x9999 };

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

void  main(void)
{
  Handle        menubarHdl;
  MenuHandle    menuHdl;
  RgnHandle     grayRgnHdl;
  EventRecord   eventStructure;

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

  doInitManagers();

  // .......................................................... create routine descriptor

  doDeviceLoopDrawUPP = NewDeviceLoopDrawingProc((ProcPtr) doDeviceLoopDraw);
  
  // .......................................................... set up menu bar and menus
  
  menubarHdl = GetNewMBar(rMenubar);
  if(menubarHdl == NULL)
    ExitToShell();
  SetMenuBar(menubarHdl);
  DrawMenuBar();

  menuHdl = GetMenuHandle(mApple);
  if(menuHdl == NULL)
    ExitToShell();
  else
    AppendResMenu(menuHdl,'DRVR');

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

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

  SetPort(gWindowPtr);
  TextSize(10);

  // .......................................... get process serial number of this process

  GetCurrentProcess(&gProcessSerNum);  

  // ............................ get window drag and sizing limits for multiple monitors 

  grayRgnHdl = LMGetGrayRgn();
  gMultiMonDragBounds = (*grayRgnHdl)->rgnBBox;
  SetRect(&gMultiMonGrowBounds,445,302,
          ((gMultiMonDragBounds.right) - (gMultiMonDragBounds.left)),
          ((gMultiMonDragBounds.bottom) - (gMultiMonDragBounds.top)));

  // .................................................................... enter eventLoop

  gDone = false;

  while(!gDone)
  {
    if(WaitNextEvent(everyEvent,&eventStructure,0,NULL))
      doEvents(&eventStructure);
    else
      doIdle();

    unloadSegments();
  }
}

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

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

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

  InitCursor();
  FlushEvents(everyEvent,0);
  
  RegisterAppearanceClient();
}

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

void  doEvents(EventRecord *eventStrucPtr)
{
  SInt16    partCode;
  WindowPtr windowPtr;
  SInt8     charCode;
  SInt32    newSize, userData;

  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,&gMultiMonDragBounds);
          break;
    
        case inGrow:
          newSize = GrowWindow(windowPtr,eventStrucPtr->where,&gMultiMonGrowBounds);
          if(newSize != 0)
            SizeWindow(windowPtr,LoWord(newSize),HiWord(newSize),true);
          InvalRect(&windowPtr->portRect);
          break;

        case inZoomIn:
        case inZoomOut:
          if(TrackBox(windowPtr,eventStrucPtr->where,partCode))
            doZoomWindowMultiMonitors(windowPtr,partCode);
          break;
      }
      break;

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

    case updateEvt:
      windowPtr = (WindowPtr) eventStrucPtr->message;

      BeginUpdate(windowPtr);
      
      if(gMultiMonitorsDrawDemo)
      {
        RGBBackColor(&gWhiteColour);
        userData = (SInt32) windowPtr;
        DeviceLoop(windowPtr->visRgn,doDeviceLoopDrawUPP,userData,0);
      }
      else if(gColourPickerDemo )
      {
        RGBBackColor(&gBlueColour);
        EraseRect(&windowPtr->portRect);
        doDrawColourPickerChoice();
      }
      else
      {
        RGBBackColor(&gBlueColour);
        EraseRect(&windowPtr->portRect);
      }
  
      EndUpdate(windowPtr);
      break;

    case osEvt:
      doOSEvent(eventStrucPtr);
      HiliteMenu(0);
      break;
  }
}

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

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

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

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

    case mFile:
      if(menuItem == iQuit)
        ExitToShell();
      break;

    case mDemonstration:
  
      gMultiMonitorsDrawDemo = gColourPickerDemo = false;
      
      switch(menuItem)
      {
        case iNotification:
          RGBBackColor(&gBlueColour);
          EraseRect(&gWindowPtr->portRect);
          doSetUpNotification();
          break;
          
        case iProgress:
          RGBBackColor(&gBlueColour);
          EraseRect(&gWindowPtr->portRect);
          doProgressIndicator();
          break;

        case iColourPicker:
          gColourPickerDemo  = true;
          doColourPicker();
          break;
          
        case iMultiMonitors:
          gMultiMonitorsDrawDemo = true;
          InvalRect(&gWindowPtr->portRect);
          break;

        case iSlotVertBlank:
          RGBBackColor(&gBlueColour);
          EraseRect(&gWindowPtr->portRect);
          doSlotVBLTask();
          break;

      }
      
      break;
  }

  HiliteMenu(0);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× unloadSegments

void  unloadSegments(void)
{
  UnloadSeg(notificationSegment);
  UnloadSeg(progressBarSegment);
  UnloadSeg(multiMonitorSegment);
  UnloadSeg(colourPickerSegment);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// Notification.c
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

#include "Miscellany.h"

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

NMRec                       gNotificationStructure;
long                        gStartingTickCount;
Boolean                     gNotificationDemoInvoked;
Boolean                     gNotificationInQueue;
Boolean                     gInBackground;
extern WindowPtr            gWindowPtr;
extern ProcessSerialNumber  gProcessSerNum;
extern RGBColor             gWhiteColour;
extern RGBColor             gBlueColour;

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× notificationSegment

void  notificationSegment(void) {}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doSetUpNotification

void  doSetUpNotification(void)
{
  doPrepareNotificationStructure();
  gNotificationDemoInvoked = true;

  gStartingTickCount = TickCount();

  RGBForeColor(&gWhiteColour);
  MoveTo(10,279);
  DrawString("\pPlease click on the desktop now to make the Finder ");
  DrawString("\pthe frontmost application.");
  MoveTo(10,292);
  DrawString("\p(This application will post a notification 10 seconds from now.)");
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××× doPrepareNotificationStructure

void  doPrepareNotificationStructure(void)
{
  Handle        iconSuiteHdl;
  Handle        soundHdl;
  StringHandle  stringHdl;

  GetIconSuite(&iconSuiteHdl,rIconFamily,kSelectorAllSmallData);
  soundHdl = GetResource('snd ',rBarkSound);
  stringHdl = GetString(rString);

  gNotificationStructure.qType    = nmType;
  gNotificationStructure.nmMark    = 1;
  gNotificationStructure.nmIcon    = iconSuiteHdl;
  gNotificationStructure.nmSound  = soundHdl;
  gNotificationStructure.nmStr    = *stringHdl;
  gNotificationStructure.nmResp    = NULL;
  gNotificationStructure.nmRefCon  = 0;
}

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

void  doIdle(void)
{
  ProcessSerialNumber  frontProcessSerNum;
  Boolean              isSameProcess;

  if(gNotificationDemoInvoked)
  {
    if(TickCount() > gStartingTickCount + 600)
    {
      GetFrontProcess(&frontProcessSerNum);
      SameProcess(&frontProcessSerNum,&gProcessSerNum,&isSameProcess);
  
      if(!isSameProcess)
      {
        NMInstall(&gNotificationStructure);
        gNotificationDemoInvoked = false;
        gNotificationInQueue = true;
      }
      else
      {
        doDisplayMessageToUser();
        gNotificationDemoInvoked = false;
      }

      EraseRect(&gWindowPtr->portRect);
    }
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doOSEvent

void  doOSEvent(EventRecord *eventStrucPtr)
{
  switch((eventStrucPtr->message >> 24) & 0x000000FF)
  {
    case suspendResumeMessage:
      gInBackground = (eventStrucPtr->message & resumeFlag) == 0;
      if((!gInBackground) && gNotificationInQueue)
        doDisplayMessageToUser();
      break;
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doDisplayMessageToUser

void  doDisplayMessageToUser(void)
{
  AlertStdAlertParamRec paramRec;
  Str255                labelText;
  Str255                narrativeText;
  SInt16                itemHit;

  if(gNotificationInQueue)
  {
    NMRemove(&gNotificationStructure);
    gNotificationInQueue = false;
  }

  EraseRect(&gWindowPtr->portRect);
  
  paramRec.movable        = true;
  paramRec.helpButton     = false;
  paramRec.filterProc     = NULL;
  paramRec.defaultText    = (StringPtr) kAlertDefaultOKText;
  paramRec.cancelText     = NULL;
  paramRec.otherText      = NULL;
  paramRec.defaultButton  = kAlertStdAlertOKButton;
  paramRec.cancelButton   = 0;
  paramRec.position       = kWindowDefaultPosition;

  GetIndString(labelText,rAlertStrings,indexLabel);
  GetIndString(narrativeText,rAlertStrings,indexNarrative);

  StandardAlert(kAlertNoteAlert,labelText,narrativeText,¶mRec,&itemHit);

  DisposeIconSuite(gNotificationStructure.nmIcon,false);
  ReleaseResource(gNotificationStructure.nmSound);
  ReleaseResource((Handle) gNotificationStructure.nmStr);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// ProgressIndicator.c
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××


#include "Miscellany.h"

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

extern WindowPtr  gWindowPtr;
extern RGBColor   gWhiteColour;

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× progressBarSegment

void  progressBarSegment(void) {}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doProgressBar

void  doProgressIndicator(void)
{
  DialogPtr     dialogPtr;
  ControlHandle progressBarHdl;
  SInt16        statusMax, statusCurrent;
  SInt16        a, b, c;
  Handle        soundHdl;
  Rect          theRect;
  RGBColor      redColour = { 0xFFFF, 0x0000, 0x0000 };
  
  if(!(dialogPtr = GetNewDialog(rDialog,NULL,(WindowPtr) -1)))
    ExitToShell();

  SetPort(dialogPtr);
  UpdateControls(dialogPtr,((GrafPtr) dialogPtr)->visRgn);
  SetPort(gWindowPtr);

  GetDialogItemAsControl(dialogPtr,iProgressIndicator,&progressBarHdl);

  statusMax = 3456;
  statusCurrent = 0;
  SetControlMaximum(progressBarHdl,statusMax);

  for(a=0;a<9;a++)
  {
    for(b=8;b<423;b+=18)
    {
      for(c=8;c<286;c+=18)
      {
        if(doCheckForCancel(dialogPtr))
        {
          soundHdl = GetResource('snd ',rBarkSound);
          SndPlay(NULL,(SndListHandle) soundHdl,false);
          ReleaseResource(soundHdl);
          DisposeDialog(dialogPtr);

          EraseRect(&gWindowPtr->portRect);
          MoveTo(10,292);
          RGBForeColor(&gWhiteColour);
          DrawString("\pOperation cancelled at user request");

          return;
        }
        
        SetRect(&theRect,b+a,c+a,b+17-a,c+17-a);
        if(a < 3)                RGBForeColor(&gWhiteColour);
        else if(a > 2 && a < 6)  RGBForeColor(&redColour);
        else if(a > 5)           RGBForeColor(&gWhiteColour);
        FrameRect(&theRect);

        SetControlValue(progressBarHdl,statusCurrent++);
      }
    }
  }

  DisposeDialog(dialogPtr);  
  EraseRect(&gWindowPtr->portRect);
  MoveTo(10,292);
  RGBForeColor(&gWhiteColour);
  DrawString("\pOperation completed");
}


// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doCheckForCancel

Boolean  doCheckForCancel(DialogPtr dialogPtr) 
{
  GrafPtr    oldPort;
  Boolean    foundCommandPeriod;
  QHdrPtr    eventQHdrPtr;
  EvQElPtr  eventQElPtr;
  SInt32    charCode;
  SInt32    commandKeyDown;

  GetPort(&oldPort);
  SetPort(dialogPtr);

  foundCommandPeriod = false;

  eventQHdrPtr = LMGetEventQueue();
  eventQElPtr = (EvQElPtr) eventQHdrPtr->qHead;

  while(eventQElPtr && !foundCommandPeriod)
  {
    if(eventQElPtr->evtQWhat == keyDown)
    {
      charCode = eventQElPtr->evtQMessage & charCodeMask;
      commandKeyDown = eventQElPtr->evtQModifiers & cmdKey;

      if(commandKeyDown)
        if(charCode == 0x2e)
          foundCommandPeriod = true;
    }

    if(!foundCommandPeriod)
      eventQElPtr = (EvQElPtr) eventQElPtr->qLink;
  }

  SetPort(oldPort);
  
  return(foundCommandPeriod);
}

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

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// ColourPicker.c
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

#include "Miscellany.h"

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

RGBColor          gInColour = { 0xCCCC, 0x0000, 0x0000 };
RGBColor          gOutColour;
Boolean           gColorPickerButton;
extern WindowPtr  gWindowPtr;
extern RGBColor   gWhiteColour;
extern RGBColor   gBlueColour;

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× colourPickerSegment

void  colourPickerSegment(void) {}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doColourPicker

void  doColourPicker(void)
{
  Rect      theRect;
  Point     where;
  Str255    prompt = "\pChoose a rectangle colour:";

  theRect = gWindowPtr->portRect;
  RGBBackColor(&gBlueColour);
  EraseRect(&theRect);
  InsetRect(&theRect,55,55);
  RGBForeColor(&gInColour);
  PaintRect(&theRect);

  where.v = where.h = 0;

  gColorPickerButton = GetColor(where,prompt,&gInColour,&gOutColour);
  
  InvalRect(&gWindowPtr->portRect);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doDrawColorPickerChoice

void  doDrawColourPickerChoice(void)
{
  Rect  theRect;
  char  *cString;  

  theRect = gWindowPtr->portRect;
  InsetRect(&theRect,55,55);

  if(gColorPickerButton)
  {
    RGBForeColor(&gOutColour);
    PaintRect(&theRect);
  
    RGBForeColor(&gWhiteColour);

    MoveTo(55,22);
    DrawString("\pRequested Red Value: ");
    cString = doDecimalToHexadecimal(gOutColour.red);
    MoveTo(170,22);
    DrawText(cString,0,6);

    MoveTo(55,35);
    DrawString("\pRequested Green Value: ");
    cString = doDecimalToHexadecimal(gOutColour.green);
    MoveTo(170,35);
    DrawText(cString,0,6);

    MoveTo(55,48);
    DrawString("\pRequested Blue Value: ");
    cString = doDecimalToHexadecimal(gOutColour.blue);
    MoveTo(170,48);
    DrawText(cString,0,6);
  }
  else
  {
    RGBForeColor(&gInColour);
    PaintRect(&theRect);
  
    RGBForeColor(&gWhiteColour);
    MoveTo(55,48);
    DrawString("\pCancel button was clicked.");
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doDecimalToHexadecimal

char  *doDecimalToHexadecimal(UInt16 decimalNumber)
{
  static char cString[] = "0xXXXX";
  char        *hexCharas = "0123456789ABCDEF";
  SInt16      a;

  for (a=0;a<4;decimalNumber >>= 4,++a)
    cString[5 - a] = hexCharas[decimalNumber & 0xF];

  return cString;
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// MultiMonitor.c
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

#include "Miscellany.h"

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× multiMonitorSegment

void  multiMonitorSegment(void) {}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doDeviceLoopDraw

pascal void  doDeviceLoopDraw(SInt16 depth,SInt16 deviceFlags,GDHandle targetDeviceHdl,
                              SInt32 userData)
{
  RGBColor  oldForeColour;
  WindowPtr windowPtr;
  Rect      theRect;
  RGBColor  greenColour  = { 0x0000, 0xAAAA, 0x1111 };
  RGBColor  redColour    = { 0xAAAA, 0x4444, 0x3333 };
  RGBColor  blueColour   = { 0x5555, 0x4444, 0xFFFF };
  RGBColor  ltGrayColour = { 0xDDDD, 0xDDDD, 0xDDDD };
  RGBColor  grayColour   = { 0x9999, 0x9999, 0x9999 };
  RGBColor  dkGrayColour = { 0x4444, 0x4444, 0x4444 };
  
  GetForeColor(&oldForeColour);

  windowPtr = (WindowPtr) userData;
  theRect = windowPtr->portRect;
  EraseRect(&windowPtr->portRect);
  
  if(BitTst(&deviceFlags,15 - gdDevType))
  {
    InsetRect(&theRect,50,50);
    RGBForeColor(&greenColour);
    PaintRect(&theRect);
    InsetRect(&theRect,40,40);
    RGBForeColor(&redColour);
    PaintRect(&theRect);
    InsetRect(&theRect,40,40);
    RGBForeColor(&blueColour);
    PaintRect(&theRect);
  }
  else
  {
    InsetRect(&theRect,50,50);
    RGBForeColor(<GrayColour);
    PaintRect(&theRect);
    InsetRect(&theRect,40,40);
    RGBForeColor(&grayColour);
    PaintRect(&theRect);
    InsetRect(&theRect,40,40);
    RGBForeColor(&dkGrayColour);
    PaintRect(&theRect);
  }

  RGBForeColor(&oldForeColour);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doZoomWindow

void doZoomWindowMultiMonitors(WindowPtr windowPtr,SInt16 zoomInOrOut)
{
  GrafPtr     oldPort;
  Rect        windRect, intersectRect, zoomRect;
  SInt16      titleBarHeight, windowFrameWidth;
  WStateData  *winStateDataPtr;
  GDHandle    deviceHdl, zoomDeviceHdl;
  SInt32      intersectArea, greatestArea;

  GetPort(&oldPort);
  SetPort(windowPtr);

  EraseRect(&windowPtr->portRect);

  if(zoomInOrOut == inZoomOut)
  {
    windRect = windowPtr->portRect;
    LocalToGlobal(&topLeft(windRect));
    LocalToGlobal(&botRight(windRect));
    titleBarHeight = windRect.top - 
                     (*((WindowPeek) windowPtr)->strucRgn)->rgnBBox.top - 1;

    windRect.top = windRect.top - titleBarHeight;

    deviceHdl = LMGetDeviceList();
    greatestArea = 0;

    while(deviceHdl != NULL)
    {
      if(TestDeviceAttribute(deviceHdl,screenDevice) && 
         TestDeviceAttribute(deviceHdl,screenActive))
      {   
        SectRect(&windRect,&(*deviceHdl)->gdRect,&intersectRect);

        intersectArea = (long) (intersectRect.right - intersectRect.left) * 
                               (intersectRect.bottom - intersectRect.top);
        if(intersectArea > greatestArea)
        {
          greatestArea = intersectArea;
          zoomDeviceHdl = deviceHdl;
        }
            
        deviceHdl = GetNextDevice(deviceHdl);
      }
    }
      
    if(zoomDeviceHdl == LMGetMainDevice())
      titleBarHeight = titleBarHeight + LMGetMBarHeight();
      
    windowFrameWidth = (*(((WindowPeek) windowPtr)->strucRgn))->rgnBBox.right -
                       (*(((WindowPeek) windowPtr)->contRgn))->rgnBBox.right;

    SetRect(&zoomRect,(*zoomDeviceHdl)->gdRect.left + 3 + windowFrameWidth,
                      (*zoomDeviceHdl)->gdRect.top + titleBarHeight + 3,
                      (*zoomDeviceHdl)->gdRect.right - 3 - windowFrameWidth,
                      (*zoomDeviceHdl)->gdRect.bottom - 3 - windowFrameWidth);
      
    winStateDataPtr = (WStateData *) *((WindowPeek) windowPtr)->dataHandle;
    winStateDataPtr->stdState = zoomRect;
  }

  ZoomWindow(windowPtr,zoomInOrOut,windowPtr == FrontWindow());
  doRedoWindowContent(windowPtr);
  SetPort(oldPort);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doRedoWindowContent

void doRedoWindowContent(WindowPtr windowPtr)
{
  // Do scroll bar and TextEdit, etc, adjustments here as appropriate.

  InvalRect(&windowPtr->portRect);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// VerticalBlank.c
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

#include "Miscellany.h"

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

VBLUPP          gSlotVBLTaskUPP;
VBLStructure    gSlotVBLStructure;
SInt16          gMainSlotNumber;

extern RGBColor   gWhiteColour;

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doSlotVBLTask

void  doSlotVBLTask(void)
{
  GDHandle    mainDeviceHdl;
  SInt16      mainDeviceRefNum;
  DCtlHandle  deviceCtlEntryHdl;
  OSErr       osErr;
  PicHandle   pictureHdl;
  Rect        theRect;
  SInt16      h = 1, v = 1, hh = 0, vv = 0, index = 0;

  gSlotVBLTaskUPP = NewVBLProc((ProcPtr) theSlotVBLTask);

  mainDeviceHdl     = LMGetMainDevice();
  mainDeviceRefNum   = (*mainDeviceHdl)->gdRefNum;
  deviceCtlEntryHdl = GetDCtlEntry(mainDeviceRefNum);
  gMainSlotNumber   = (SInt16) (*((AuxDCEHandle) deviceCtlEntryHdl))->dCtlSlot;

  RGBForeColor(&gWhiteColour);

  osErr = doInstallSlotVBLTask();
  if(osErr != noErr)
  {
    MoveTo(10,292);
    DrawString("\pCould not install slot-based VBL task.");
    DisposeRoutineDescriptor(gSlotVBLTaskUPP);
    return;
  }
    
  MoveTo(10,292);
  DrawString("\pClick mouse to remove slot-based VBL task");
  pictureHdl = GetPicture(rPicture);
  theRect = (*pictureHdl)->picFrame;

  while(!Button())
  {
    while(!gSlotVBLStructure.inVBlankPeriod)
      ;
      
    OffsetRect(&theRect,h,v);
    DrawPicture(pictureHdl,&theRect);

    gSlotVBLStructure.inVBlankPeriod = false;

    hh = hh + h;
    if(hh == 0 || hh == 350)
      h = -h;
    vv = vv + v;
    if(vv == - 18 || vv == 165)
      v = -v;
  }
  
  doStopSlotVBLTask();
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doInstallSlotVBLTask

OSErr doInstallSlotVBLTask(void)
{
  OSErr  osErr;

  gSlotVBLStructure.vblTaskStruc.qType    = vType;
  gSlotVBLStructure.vblTaskStruc.vblAddr  = gSlotVBLTaskUPP;
  gSlotVBLStructure.vblTaskStruc.vblCount  = 1;
  gSlotVBLStructure.vblTaskStruc.vblPhase  = 0;
  gSlotVBLStructure.inVBlankPeriod = false;

  osErr = SlotVInstall((QElemPtr) &gSlotVBLStructure.vblTaskStruc,gMainSlotNumber);

  return osErr;
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× theSlotVBLTask

#if TARGET_CPU_68K
void  theSlotVBLTask(void)
#else
void  theSlotVBLTask(VBLStructurePtr vblStructurePtr)
#endif
{
#if TARGET_CPU_68K
  VBLStructurePtr  vblStructurePtr;

  vblStructurePtr = (VBLStructurePtr) GetVBLRec();
#endif

  vblStructurePtr->inVBlankPeriod = true;
  vblStructurePtr->vblTaskStruc.vblCount = 1;
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doStopSlotVBLTask

void  doStopSlotVBLTask(void)
{
  SlotVRemove((QElemPtr) &gSlotVBLStructure.vblTaskStruc,gMainSlotNumber);

  DisposeRoutineDescriptor(gSlotVBLTaskUPP);
}

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

Demonstration Program Comments

When this program is run, the user should make choices from the Demonstration menu,
taking the following actions and making the following observations:

*  Choose the Notification item and, observing the instructions in the window, click 
   the desktop immediately to make the Finder the foreground application.  A 
   notification will be posted by Miscellany about 10 seconds after the Notification 
   item choice is made.  Note that, when about 10 seconds have elapsed, the 
   Notification Manager invokes an alert box (Mac OS 8.6 and earlier) or floating 
   window (Mac OS 9.x) and alternates the Finder and Miscellany icons in the 
   Application menu title.  Observing the instructions in the alert box/floating 
   window, dismiss the alert (Mac OS 8.6 and earlier only) and then choose the 
   Miscellany item in the Application menu, noting the ¨ mark to the left of the item
   name. When Miscellany comes to the foreground, note that the icon alternation 
   concludes and that an alert (invoked by Miscellany) appears.  Dismiss this second 
   alert box.

*   Choose the Notification item again and, this time, leave Miscellany in the
    foreground.  Note that only the alert box invoked by Miscellany appears on this 
    occasion.

*   Choose the Notification item again and, this time, click on the desktop and then
    in the Miscellany window before 10 seconds elapse.  Note again that only the alert
    box invoked by Miscellany appears.

*   Choose the Determinate Progress Indicator item, noting that the progress indicator
    dialog box is automatically disposed of when the (simulated) time-consuming task
    concludes.

*   Choose the Determinate Progress Indicator item again, and this time press the
    Command-period key combination before the (simulated) time-consuming task
    concludes.  Note that the progress indicator dialog box is disposed of when the 
    Command-period key combination is pressed.

*   Choose the Colour Picker item and make colour choices using the various available
    modes.  Note that, when the Colour Picker is dismissed by clicking the OK button,
    the requested RGB colour values for the chosen colour are displayed in hexadecimal,
    together with a rectangle in that colour, in the Miscellany window.

*   Choose the Multiple Monitors Draw item, noting that the drawing of the simple
    demonstration image is optimised as follows:

    *   On a monitor set to display 256 or more colours, the image is drawn in three
        distinct colours.  The luminance of the three colours is identical, meaning
        that, if these colours are drawn on a grayscale screen, they will all appear
        in the same shade of gray.

    *   On a monitor set to display 256 shades of gray, the image is drawn in three
        distinct shades of gray.

*   Choose the Slot-Based VBL Task item, bearing in mind that the successive re-drawing
    of the picture is delayed until the monitor is in the vertical blank period.

In addition, if the user's system has more than one monitor, the user should zoom the
window in and out when the window is on the main monitor, when it has been dragged to the
second monitor, and when it has been dragged to a position where it is partially
displayed on both monitors, noting the standard state, and the monitor zoomed to, in each
case.

Miscellany.h

#typedef

The VBLStructure data type, which can be regarded as an expanded VBL task structure,
will be used by the slot-based VBL task.  The first field is a VBL task structure.  The 
second field will be used to save the A5 register (680x0 code only).

Miscellany.c

Global Variables

doDeviceLoopDrawUPP will be assigned a universal procedure pointer to the
application-defined image-optimising drawing function called by DeviceLoop. 
gProcessSerNum will be assigned the process serial number of the Miscellany application. 
gMultiMonDragBounds will be passed in the bounds parameter of the DragWindow function. 
gMultiMonGrowBounds will be passed in the bBox parameter of the GrowWindow
function.

main

The call to NewDeviceLoopDrawingProc creates a routine descriptor for the
image-optimising drawing function doDeviceLoopDraw.

GetCurrentProcess gets the process serial number of this process.

The next block defines two rectangles. gMultiMonDragBounds is set to equate to the
current desktop region, which potentially crosses multiple devices and consists of
everything below the menu bar.  This establishes the limits within which the user will be
able to drag the window.  gMultiMonGrowBounds establishes the minimum and maximum heights
and widths to apply to window resizing.

Within the event loop, note that:

*   The call to doIdle is relevant to the notification demonstration only.

*   The application-defined function unloadSegments is called at the bottom of the event
    loop after the event received by WaitNextEvent has been handled to completion.

doEvents

Note that, within the mouseDown case, gMultiMonDragBounds is be passed in the bounds
parameter of DragWindow, gMultiMonGrowBounds is passed in the bBox parameter of
GrowWindow, and the application-defined window-zooming function doZoomWindowMultiMonitors
is called at the inZoomIn/inZoomOut case.

Within the updateEvt case, if the Multiple Monitors Draw item in the Demonstration menu
has been chosen (gMultiMonitorsDrawDemo is true), a call is made to DeviceLoop and the
universal procedure pointer to the application-defined drawing function doDeviceLoopDraw
is passed as the second parameter.

doMenuChoice

When the Multiple Monitors Draw item in the Demonstration menu is chosen, the 
window's port rectangle is invalidated so as to force an update event and consequential 
call to DeviceLoop.

unloadSegments

unloadSegments unlocks, and marks as purgeable, the specified 68K code segments,
that is, the segment in which a stub ("do nothing" function) resides.  Note that a stub
has not been included in the segment containing the VBL task demonstration code, and that
that segment is preloaded and locked.  This is because a VBL task's code must be locked
into physical memory if virtual memory is in operation.

Recall that there is no need to include conditional compilation directives in source code
containing segmentation directives before that code is compiled for the PowerPC. 
Compilers which produce PowerPC code ignore segmentation directives, and any calls to the
Segment Manager's UnloadSeg function are simply ignored.

Notification.c

notificationSegment

notificationSegment is the stub, or "do nothing" function, called by unloadSegments
at the bottom of the main event loop.

doSetUpNotification

doSetUpNotification is called when the user chooses Notification from the
Demonstration menu.

The first line calls an application-defined function which fills in the relevant fields
of a notification structure.  The next line assigns true to a global variable which
records that the Notification item has been chosen by the user.

The next line saves the system tick count at the time that the user chose the
Notification item.  This value is used later to determine when 10 seconds have elapsed
following the execution of this line.

doPrepareNotificationStructure

doPrepareNotificationStructure fills in the relevant fields of the notification
structure.

First, however, GetIconSuite creates an icon family based on the specified resource ID
and the third parameter, which limits the family to 'ics#', 'ics4' and 'ics8' icons.  The
GetIconSuite call returns the handle to the suite in its first parameter.  The call to
GetResource loads the specified 'snd ' resource.  GetString loads the specified 'STR '
resource.

The first line of the main block specifies the type of operating system queue.  The next
line specifies that the ¨ mark is to appear next to the application's name in the
Application menu.  The next three lines assign the icon suite, sound and string handles
previously obtained.  The next line specifies that no response function is required to be
executed when the notification is posted.

doIdle

doIdle is called from the main event loop when a null event is received.  Recall
that the canBackground flag is set in the application's 'SIZE' resource, meaning that the
application will receive null events when it is in the background.

If the user has not just chosen the Notification item in the Demonstration menu
(gNotificationDemoInvoked is false), doIdle simply returns immediately.

If, however, that item has just been chosen, and if 10 seconds (600 ticks) have elapsed
since that choice was made, the following occurs:

*   The calls to GetFrontProcess and SameProcess determine whether the current
    foreground process is Miscellany.  If it is not, the notification request is
    installed in the notification queue by NMInstall and a global variable is set to
    indicate that a request has been placed in the queue by Miscellany.  Also,
    gNotificationDemoInvoked is set to false so as to ensure that the main if block
    only executes once after the Notification item is chosen.

*   If, however, the current foreground process is Miscellany, the application-defined
    function doDisplayMessageToUser is called to present the required message to the
    user, via an alert box, in the normal way.  Once again gNotificationDemoInvoked is
    reset to false so as to ensure that the main if block only executes once after the
    Notification item is chosen.

doOSEvent

doOSEvent handles operating system events.

If the event is a resume event (that is, Miscellany is coming to the foreground) and if
the notification request is still in the notification queue (gNotificationInQueue is
true), the application-defined function doDisplayMessageToUser is called to remove the
notification request from the queue and have Miscellany convey the required message to
the user via an alert box.

doDisplayMessageToUser

doDisplayMessageToUser is called by doOSEvent and doIdle in the circumstances
previously described.

If a Miscellany notification request is in the queue, NMRemove removes it from the queue
and gNotificationInQueue is set to false to reflect this condition.  (Recall that, if the
nmResp field of the notification structure is not assigned -1, the application itself
must remove the queue element from the queue.)

Regardless of whether there was a notification in the queue or not, Miscellany then
presents its alert.  When the alert is dismissed, the notification's icon suite, sound
and string resources are released/disposed of.

ProgressIndicator.c

doProgressIndicator

doProgressIndicator is called when the user chooses Determinate Progress Indicator
from the Demonstration menu.

GetNewDialog creates a modal dialog box.  The call to UpdateControls draws the dialog
box's controls.  The call to GetDialogItemAsControl retrieves the handle to the dialog's
progress indicator control.  SetControlMaximum sets the control's maximum value to equate
to the number of steps in a simulated time-consuming task.

The main for loop performs the simulated time-consuming task, represented to the user by
the drawing of a large number of coloured rectangles in the window.  The task involves
3456 calls to FrameRect.

Within the outer for loop, the application-defined function doCheckForCancel is called to
check whether the user has pressed the Command-period key combination or clicked the
dialog's Stop button.  If so, a 'snd ' resource is loaded, played, and released, the
dialog is disposed of, an advisory message in drawn in the window, and the function
returns.

Within the inner for loop, the rectangles are drawn.  Each time round this inner loop, a
progress indicator control's value is incremented.

When the outer loop exits (that is, when the Command-period key combination is not
pressed before the simulated time-consuming task completes), the dialog is disposed of.

doCheckForCancel

doCheckForCancel scans the event queue for Command-period keyboard events.

The first line sets a variable so as to begin by assuming that such an event is not in 
the queue.

LMGetEventQueue gets a pointer to the event queue header.  The next line gets a pointer 
to first queue element.  The while loop scans the whole of the event queue, exiting only 
when a Command-period key event is found in the queue or the entire queue has been
scanned.

Inside the loop, if a key-down event is found, the first two lines in the else if 
block get the character code and the third line checks whether the Command key was down.
If the Command key was down, and if the character code is the code for the period 
character (charCode 0x2e), the variable foundCommandPeriod is set to true, causing the 
loop to exit and doCheckForCancel to return true.  

If a Command-period keyboard event was not found, the last two lines in the while loop
call up the next queue for examination.

The last line returns the result of the search.

ColourPicker.c

doColourPicker

doColourPicker is called when the user chooses Colour Picker from the Demonstration
menu.

The first block erases the window's content area and paints a rectangle in the colour
which will be passed in GetColor's inColor parameter. 

The next line assigns 0 to the fields of the Point variable to be passed in GetColor's
where parameter.  ((0,0) will cause the Colour Picker dialog box to be centred on the
main screen.)

The call to GetColor displays the Colour Picker's dialog box.  GetColor retains control
until the user clicks either the OK button or the Cancel button, at which time the port
rectangle is invalidated, causing the function doDrawColourPickerChoice to be
called.

doDrawColourPickerChoice

If the user clicked the OK button, a filled rectangle is painted in the window in
the colour returned in GetColor's outColor parameter, and the values representing the
red, green, and blue components of this colour are displayed at the top of the window in
hexadecimal.  Note that the application-defined function doDecimalToHexadecimal is called
to convert the decimal (UInt32) values in the fields of the RGBColor variable outColor to
hexadecimal.

If the user clicks the Cancel button, a filled rectangle is painted in the window in the
colour passed in GetColor's inColor parameter.

doDecimalToHexadecimal

doDecimalToHexadecimal converts a UInt16 value to a hexadecimal string.
MultiMonitor.c

doDeviceLoopDraw

doDeviceLoopDraw is the image-optimising drawing function the universal procedure
pointer to which is passed in the second parameter in the DeviceLoop call in the function
doEvents.  (Recall that the DeviceLoop call is made whenever the Multiple Monitors Draw
item in the Demonstration menu has been selected and an update event is received.) 
DeviceLoop scans all active video devices, calling doDeviceLoopDraw whenever it
encounters a device which intersects the drawing region, and passing certain information
to doDeviceLoopDraw.

The second line casts the SInt32 value received in the userData parameter to a WindowPtr. 
The window's content area is then erased.

If an examination of the device's attributes, as received in the deviceFlags formal
parameter, reveals that the device is a colour device, three rectangles are painted in
the window in three different colours.  (The luminance value of these colours is the
same, meaning that the rectangles would all be the same shade of gray if they were drawn
on a monochrome (grayscale) device.) 

If the examination of the device's attributes reveals that the device is a monochrome
device, the rectangles are painted in three distinct shades of gray.

doZoomWindowMultiMonitors

doZoomWindowMultiMonitors is called when the user clicks in the window's zoom box.

The first two lines save and set the current graphics port.  The third line erases the
window's port rectangle prior to the zoom so as to avoid flicker.  

The main if block executes only if the direction of the zoom is "out" to the standard
state.  The purpose of this block of code is to determine the standard state rectangle
and, in a multi-monitors environment, the monitor on which the zoomed window is to be
displayed.

The first block inside the if block converts the window's port rectangle to global
coordinates and gets the height of the window's title bar.  The next line establishes a
rectangle equal to the window's port rectangle, plus the window's title bar, in global
coordinates.  

LMGetDeviceList gets a handle to the first gDevice structure in the device list and the
variable greatestArea is assigned 0.  

The while loop then walks the device list.  For each active video device, the associated
gDevice structure's gdRect field is compared to the window's rectangle by a call to
SectRect.  If the two rectangles intersect, the coordinates of the intersection are
assigned to the intersectRect variable, otherwise an empty rectangle ((0,0)(0,0)) is
assigned to intersectRect.  The area of the intersection rectangle is calculated and
stored in the variable intersectArea.  If the new value in intersectArea is greater than
that calculated during any previous pass through the loop, the variable zoomDeviceHdl is
assigned the GDHandle of the device currently being examined.

GetNextDevice gets the handle to the next device in the device list.  The while loop
exits when this call returns NULL.  When the loop exits, the contents of the variable
zoomDeviceHdl represents the video device on which the window should be zoomed to the
standard state, that is, the device on which the largest area of the window currently
appears.

If the call to LMGetMainDevice reveals that this device is the main device, the height of
the menu bar is added to the value in the variable which holds the window's title bar
height.

The next two lines determine the width of the window frame and the following four lines
establish the standard state rectangle.  This latter is three pixels inside the rectangle
contained in the gdRect field of the device's gDevice structure, but with the top
adjusted to account for the height of the title bar (and the menu bar if the device is
the main device) and the sides and bottom adjusted for the width of the window frame. 
The last two lines in the main if block then assign this rectangle to the stdState field
of the window's state data structure.

Below the main if block, ZoomWindow is called to zoom the window in the appropriate
direction, following which an application-defined function is called to redraw the window
contents as appropriate.  Finally, the saved graphics port is restored.

doRedoWindowContent

doRedoWindowContent is called by doZoomWindowMultiMonitors to redraw the content
region of a newly-zoomed window.  In this demonstration the window's content area is
simply invalidated, forcing an update event.

VerticalBlank.c

doSlotVBLTask

doSlotVBLTask is called when the user chooses Slot-Based VBL Task from the
Demonstration menu. In this demonstration, the slot-based VBL task is used to delay the
drawing of a picture at a new location until the monitor enters the vertical blank
period.

The first line creates a routine descriptor for the slot-based VBL task.

The next block gets the slot number of the main graphics device.  This process involves
getting a handle to the startup gDevice structure, extracting from that structure the
device driver's reference number, getting a handle to the DCtlEntry structure, and then
getting the slot number.

doInstallSlotVBLTask is then called to install the slot-based VBL task.  If this call is
not successful, the function returns.

The call to GetPicture loads the specified 'PICT' resource, following which the picFrame
field in the resource is copied to a local variable.  Within the outer while loop, this
rectangle is continually offset between successive calls to DrawPicture, causing the
picture to appear to move around the window.  The inner (empty) while loop continues to
cycle until the inBlankPeriod field of the expanded VBL task structure is set to true by
the VBL task, at which time the Picture is drawn and the inBlankPeriod field is set back
to false.

When the user clicks the mouse, the outer while loop exits and doStopSlotVBLTask is
called to remove the slot-based VBL task.

doInstallSlotVBLTask

doInstallSlotVBLTask installs the slot-based VBL task.

The first four lines fill in the appropriate fields of the VBL task record.  Note that
the vblCount field is set to 1 so that the VBL task will execute at the first interrupt.

The fifth line sets a field in the expanded VBL task structure to false.  This field will
be set to true by the slot-based VBL task.  Note that there is no need to save the
application's A5 in this case because the slot-based VBL task does not need to access an
application global variable.

SlotVInstall attempts to install the task in the slot-based queue.  The success, or
otherwise, of the attempted installation is returned to the calling function.

theSlotVBLTask

theSlotVBLTask is the slot-based VBL task itself.  It is similar to theSystemVBLTask
except that no measures are required to facilitate access to the application's global
variables.

When the task executes, it sets to true the flag in the expanded VBL task structure which
indicates that the vertical blanking period has just been entered.  When the task
executes, the value in the vblCount field of the VBL task record will be 0.  So that the
task will execute again at the next interrupt, the last line  sets the vblCount field of
the VBL task record back to 1.

doStopSlotVBLTask

doStopSlotVBLTask is called when the user clicks the mouse button.  It removes the
slot-based VBL task from the relevant queue and disposes of the routine descriptor.

Recall that, at the Demonstration Program Controls2 Comments section at
Chapter7 - Introduction to Controls, mention was made of horizontal "tearing"
of the picture in the lower section of the window when it was being scrolled
using the scroll arrows, and that this would be addressed in the Demonstration
Program Comments section of this chapter.

This "tearing" can be eliminated by delaying the drawing of the picture
until the vertical blanking period is entered, using the same approach as is
used in the slot-based VBL task demonstration, above.  Simply install a 
slot-based VBL task using the relevant source code in the Miscellany 
demonstration and change the bottom of the actionFuncLive function in 
Controls2.c to the following:

    doMoveScrollBox(controlHdl,scrollDistance);
  }
  
  SetOrigin(GetControlValue((*docStrucHdl)->scrollbarLiveHdl),0);

  if(!gVBLInstallFail)  // Set this flag to true if VBL task is not 
                        // successfully installed.
  {
    while(!gSlotVBLStructure.inVBlankPeriod) // Wait for vert blanking period.
      ;
    DrawPicture(gPictHandleLive,&gPictRectLive);
    gSlotVBLStructure.inVBlankPeriod = false;
  }
  else
    DrawPicture(gPictHandleLive,&gPictRectLive);

  SetOrigin(0,0);
}

 

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

You can save $300-$480 on a 14-inch M3 Pro/Ma...
Apple has 14″ M3 Pro and M3 Max MacBook Pros in stock today and available, Certified Refurbished, starting at $1699 and ranging up to $480 off MSRP. Each model features a new outer case, shipping is... Read more
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

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.