TweetFollow Us on Twitter

MACINTOSH C: A Hobbyist's Guide To Programming the Mac OS in C
Version 2.3

2000 K. J. Bricknell

Go to Contents

(Chapter 23)


A link to the associated demonstration program listing is at the bottom of this page

Notification From Applications in the Background

The Need for the Notification Manager

Applications running in the background cannot use the standard methods of communicating with the user, such as alert or dialog boxes, because such windows might easily be obscured by the windows of other applications. Furthermore, even if these windows are visible, the background application cannot be certain that the user has actually received the communication. Accordingly, some more reliable method must be used to manage communication between a background application and the user. The Notification Manager provides such a method.

Elements of a Notification

The Notification Manager creates notifications. A notification comprises one or more of five possible elements, which occur in the following sequence:

  • A diamond mark appears against the name of the target application in the Application menu.

    This mark is intended to prompt the user to switch the marked application to the foreground. The diamond mark only appears while the application posting the notification remains in the background. It is replaced by the familiar check mark when that application is brought to the foreground.

  • The Application menu title begins alternating between the target application's icon and the foreground application's icon, or the Apple menu title begins alternating between the target application's icon and the Apple icon.

    The location of the icon alternation in the menu bar is determined by the posting application's mark (if any). If the application posting the notification is marked by either a diamond mark or a check mark in the Application menu, the Application menu title alternates; otherwise the Apple menu title alternates. Note that several applications might post notifications, so there might be a series of alternating icons.

  • The Sound Manager plays a sound.

    The application posting the notification can request that the system alert sound be used or it can specify its own sound by passing the Notification Manager a handle to a 'snd ' resource.

  • In Mac OS 8.6 and earlier, a modal alert box appears, and the user dismisses it (by clicking on the Cancel button). In Mac OS 9.x, a floating window appears, allowing the current application's event loop to continue running while the notification alert is on the screen.

    The application posting the notification specifies the text for the modal alert box.

  • A response function, if specified, executes.

    A response function can be used to remove the notification request from the notification queue (see below) or to perform other processing. For example, it can be used to set a global variable to record that the notification was received.

Suggested Notification Strategy

Apple's suggested notification strategy is to allow the user to set the desired level of notification at one of three levels, as follows:

  • Level 1. Display the diamond mark next to the name of the application in the Application menu.

    Note that displaying the diamond mark is only possible if the requesting software is listed in the Application Menu (and thus represents a process which is loaded into memory). The requesting software may not be an application. In addition to applications, other software that is largely invisible to the user can use the Notification Manager. Such software includes device drivers, vertical blanking (VBL) tasks, Time Manager tasks, and code which executes during the system startup sequence, such as code contained in extensions.

  • Level 2. Display the diamond mark next to the name of the application in the Application menu and alternate the icons. (This is the suggested default setting.)

  • Level 3. Display the diamond mark next to the name of the application in the Application menu, alternate the icons and invoke an alert box to notify the user that something needs to be done.
A sound might also be played at levels 2 and 3, but the user should have the option of turning the sound off. In addition, the user should be provided with the option of turning notification off altogether, except in cases where damage might occur or data would be lost.

That said, Apple accepts that this suggested strategy might not be appropriate for your application. (Indeed, notifications provided by the system software itself do not follow these guidelines.)

Notifications in Action


The Notification Manager is automatically initialised at system startup.

To issue a notification to the user, you need to create a notification request and install it into the notification queue, which is a standard Macintosh queue. The Notification Manager interprets the request and presents the notification to the user at the earliest possible time.

Eventually, you will need to remove the notification request from the notification queue. You can do this in the response function or when your application returns to the foreground.

Creating a Notification Request

The Notification Structure

When installing a request into the notification queue, your application must supply a pointer to a notification structure, a static and nonrelocatable structure of type NMRec which indicates the type of notification you require. Each entry in the notification queue is, in fact, a notification structure. The notification structure is as follows:

     struct NMRec
       QElemPtr  qLink;       // Address of next element in queue. (Used internally.)
       short     qType;       // Type of data. (8 = nmType).
       short     nmFlags;     // (Reserved.)
       long      nmPrivate;   // (Reserved.)
       short     nmReserved;  // (Reserved.)
       short     nmMark;      // Application to identify with _ mark.
       Handle    nmIcon;      // Handle to small icon.
       Handle    nmSound;     // Handle to sound structure.
       StringPtr nmStr;       // Pointer to string to appear in alert.
       NMUPP     nmResp;      // Pointer to response function.
       long      nmRefCon;    // Available for application use.
     typedef struct NMRec NMRec;
     typedef NMRec *NMRecPtr;

Field Descriptions

To set up a notification request, you need to fill in at least the first six of the following fields:

qType Indicates the type of operating system queue. Set to nmType (8).
nmMark Indicates whether to place a diamond mark next to the name of the application in the Application menu. If nmMark is 0, no mark appears. If nmMark is 1, the mark appears next to the name of the calling application. If nmMark is neither 0 nor 1, it is interpreted as the reference number of a desk accessory. An application should set nmMark to 1 and a driver or detached background task (such as a VBL task or Time Manager task) should set nmMark to 0.
nmIcon A handle to an icon family containing a small colour icon, that is to alternate periodically in the menu bar. If nmIcon is set to NULL, no icon appears in the menu bar. This handle must be valid at the time the notification occurs. It does not need to be locked, but it must be non-purgeable.
nmSound A handle to a sound resource to be played with SndPlay. If nmSound is set to NULL, no sound is produced. If nmSound is set to -1, the system alert sound is played. This handle does not need to be locked, but it must be non-purgeable.
nmStr Points to a string which appears in the alert box. If nmStr is set to NULL, no alert box appears. Note that the Notification Manager does not make a copy of this string, so your application should not dispose of this storage until it removes the notification request.
nmResp A universal procedure pointer to a response function. If nmResp is set to NULL, no response function executes when the notification is posted. If nmResp is set to -1, then a pre-defined function removes the notification request immediately after it has completed.

If you do not need to do any processing in response to the notification, you should set nmResp to NULL. If you supply the universal procedure pointer to your own response function, the Notification Manager passes it one parameter: a universal procedure pointer to your notification structure. For example, this is how you would declare a response function having the name theResponse:

     pascal void  theResponse(NMUPP nmStructurePtr);

You can use response functions to remove notification requests from the notification queue, free any memory, or set a global variable in your application to record that the notification was posted.

Note that an nmResp value of -1 does not free the memory block containing the queue element; it merely removes that element from the notification queue.
When the Notification Manager calls your response function, it does not set up A5 or low-memory globals for you. If you need to access your application's global variables, you should save its A5 in the nmRefCon field.

If you are setting a global variable to enable you to determine that the user actually received the notification, you need to request an alert notification. This is because the response function executes only after the user has clicked the OK button in the alert box.

If you choose audible or alert notifications, you should probably set nmResp to -1 so that the notification structure is removed from the queue as soon as the sound has finished or the user has dismissed the alert box. However, if either nmMark or nmIcon is non-zero, do not set nmResp to -1, because the Notification Manager will remove the diamond mark or the icon before the user sees it.

nmRefCon A long integer available for your application's own use.

Installing a Notification Request

NMInstall is used to add a notification request to the notification queue. The following is an example call:

     osErr = NMInstall(¬ificationStructure);

Before calling NMInstall, you should make sure that your application is running in the background. If your application is in the foreground, you simply use standard alert methods, rather than the Notification Manager, to gain the user's attention.

Removing a Notification Request

NMRemove is used to remove a notification request from the notification queue. The following is an example call:

     osErr = NMRemove(¬ificationStructure);

You can remove requests at any time, either before or after the notification actually occurs.

As previously stated, in Mac OS 9.x, notifications are non-blocking, meaning that the user can activate the posting application without dismissing the alert. For this reason, when your application is running on Mac OS 9.x, may wish to have it explicitly cancel an alert notification using NMRemove when the application becomes active.

Progress Bars and Scanning for Command-Period Key-Down Events and Mouse-Down Events

Progress Bars

Operations within an application which tie up the machine for relatively brief periods of time should be accompanied by a cursor shape change to the watch cursor, or perhaps to an animated cursor. On the other hand, lengthy operations should be accompanied by the display of a progress indicator.

The progress indicator control was described at Chapter 14 - More On Controls. A progress indicator created using this control may be determinate or indeterminate. Determinate progress indicators show how much of the operation has been completed. Indeterminate progress indicators show that an operation is occurring but does not indicate its duration. Ordinarily, progress indicators should be displayed within a dialog box.

As stated at Chapter 2 - Low and Operating System Events, your application should allow the user to cancel a lengthy operation using the Command-period key combination. You might also include a Stop push button in the dialog box in which the progress indicator is located.

Scanning for Command-Period Key-Down Events and Mouse-Down Events in a Stop Button

One way to satisfy this requirement is to periodically call an application-defined function which scans the event queue for Command-period key-down events and mouse-down events. This function should return true if:

  • A Command-period keyboard event is found.

  • A mouse-down event is found and the mouse-down was within the Stop button's rectangle.
The application-defined function should first get a pointer to the first queue element. It should then scan the queue for key-down and mouse-down events.

If a key-down event is found, the next step is to determine whether the Command key was down at the time of the key press. If it was, a check should be made as to whether the key pressed was the period key. If these checks reveal that a Command-period keyboard event has occurred, the function should return immediately, returning true to the calling function.

If a mouse-down event is found, the next step is to determine whether the mouse-down was within the Stop push button's rectangle and, if so, briefly highlight the push button before returning true to the calling function.

If true is returned to the calling function, that function should terminate the lengthy operation and close the progress indicator dialog box.

Soliciting a Colour Choice From the User - The Color Picker

The Color Picker Utilities provide your application with:

  • A standard dialog box, called the Color Picker, for soliciting a colour choice from the user.

  • Functions for converting colour specifications from one colour model to another.

Preamble - Colour Models

In the world of colour, three main colour models are used to specify a particular colour. These are the RGB (red, green, blue) model, the CYMK (cyan, magenta, yellow, black) model, and the HLS or HSV (hue, lightness, saturation, or hue, saturation, value) models.

RGB Model

The RGB model is used where light-produced colours are involved, as in the case of a television set, computer monitor, or stage lighting. In this model, the three primary colours involved (red, green, and blue) are said to be additive because, the more of each colour you add, the closer the resulting colour is to white.

CYMK Model

The CYMK model is closely associated with printing, that is, putting colour on a white page. In this model, the three primary colours (cyan, yellow, and magenta) are said to be subtractive because, the more of each colour you add, the closer the resulting colour is to black. (The inclusion of black in the model accounts for the fact that the colours of printer's inks may vary slightly from true cyan, yellow, and magenta, meaning that a true black may not be achievable with just a CYM model.)

Cyan, magenta, and yellow are the complements of red, green, and blue.

HLS and HSV Models

The HLS and HSV models separate colour (that is, hue) from saturation and brightness. Saturation is a measure of the amount of white in a colour (the less white, the more saturated the colour). Lightness is the measure of the amount of black in a colour. (The less black, the lighter the colour). The amount of black is specified by the lightness (L) value in the HLS model and by the value (V) value in the HSV model.

The HSL/HLV model may be represented diagrammatically by the HSL/HLV colour cone shown at Fig 1. In this colour cone, hue is represented by an angle between 0 degrees and 360 degrees.

(Colour cone)

The Color Picker

The Color Picker allows the user to specify a colour using either the RGB, CMYK, HLS, or HSV, models.

Using the Color Picker RGB Mode

Fig 2 shows the Color Picker in RGB mode. The desired red, green and blue values may be set using the three slider controls or may be entered directly into the edit text fields on the right of the sliders.

(Color picker in RGB mode)

Using the Color Picker in HLS Mode

Fig 3 shows the Color Picker in HLS mode. Hue is specified by an angle, which may be entered at Hue Angle:. Saturation is specified by percentage, which may be entered at Saturation:. Lightness is also specified by a percentage, which may be entered at Lightness: Alternatively, hue and saturation may be selected simultaneously by clicking at the desired point within the coloured disc, and lightness may be set with the slider control.

To relate Fig 3 to Fig 1, the coloured disc at Fig 3 may be considered as the HSL/HSV cone as viewed from above. The lightness slider control can then be conceived of as moving the disc up or down the axis of the cone from the apex (black) to the base (white).

(Color Picker in HLS mode)

Invoking the Color Picker

The Color Picker is invoked using the GetColor function:

     Boolean GetColor(Point where,ConstStr255Param prompt,const RGBColor *inColor,
                      RGBColor *outColor);

where Dialog's upper-left corner. (0,0) causes the dialog box to positioned centrally on the main screen.
prompt A prompt string, which is displayed in the upper left corner of the main pane in the dialog box.
inColor The starting colour, which the user may want for comparison, and which is displayed against Original: in the top right corner of the dialog box.
outColor Initially set to equal inColor. Assigned a new value when the user picks a colour. The colour stored in this parameter is displayed at the top right of the dialog box against New:.)
Returns: A Boolean value indicating whether the user clicked on the OK button or Cancel button.

If the user clicks the OK button in the Color Picker dialog, your application should adopt the outColor value as the colour chosen by the user. If the user clicks the Cancel button, your application should assume that the user has decided to make no colour change, that is, the colour should remain as that represented by the inColor parameter.

Coping With Multiple Monitors


Many Macintosh models can accommodate more than one monitor. In a multi-monitor system, the Monitors control panel allows the user to specify which of the attached monitors is to be the main screen (that is, the screen containing the menu bar) and to set the position of the other screen, or screens, relative to the main screen.

The maximum number of colours capable of being displayed by a given Macintosh at the one time is determined by the video capability of that particular Macintosh. The maximum number of colours capable of being displayed on a given screen at the one time depends on settings made by the user using the Monitors and Sound control panel. In a multi-monitor environment, therefore, it is possible for each screen to be set to a different pixel depth.

In more technical terms, a Monitors control panel colours/grays setting sets the pixel depth of a particular video device. A brief review of the subject of video devices is therefore appropriate at this point.

Video Devices Revisited

As stated at Chapter 11 - QuickDraw Preliminaries:

  • A graphics device is anything into which QuickDraw can draw, a video device (such as a plug-in video card or a built-in video interface) is a graphics device that controls screens, Color QuickDraw stores information about video devices in GDevice structures, the system creates and initialises a GDevice structure for each video device found during start-up, all structures are linked together in a list called the device list, and the global variable DeviceList holds a handle to the first structure in the list.

    The Monitors and Sound control panel stores the pixel depth and other configuration information in a resource of type 'scrn' (resource ID 0). This resource contains an array of data structures which are analogous to GDevice records. Each element of this array contains information about a different video device. When InitGraf is called to initialize QuickDraw, it checks the System file for the 'scrn' resource. If the resource is found, and if it matches the hardware, InitGraf organises the video devices according to the resource's contents. If the resource is not found, QuickDraw uses only the video device of the startup screen.

  • At any given time, one, and only one, graphics device is the current device, that is, the one in which the drawing is taking place. A handle to the current device's GDevice structure is placed in the global variable TheGDevice.

    The current device is sometimes referred to as the active device.

By default, the GDevice structure corresponding to the first video device found at start up is marked as the (initial) current device, and all other graphics devices in the list are initially marked as inactive. When the user moves a window to, or creates a window on, another screen, and your application draws into that window, Color QuickDraw automatically makes the video device for that screen the current device and stores that information in TheGDevice. As Color QuickDraw draws across a user's video devices, it keeps switching to the GDevice structure for the video device on which it is actively drawing.

Also recall from Chapter 11 - QuickDraw Preliminaries that two of the fields in a GDevice structure are:

  • gdMap, which contains a handle to a pixel map which, in turn, contains a field (pixelSize) containing the device's pixel depth (that is, the number of bits per pixel).

  • gdRect, which contains the device's global boundaries.

Requirements of the Application

Accommodating a multi-monitor environment requires that you address the following issues:

  • Image Optimisation. To draw a particular graphic, your application may have to call different drawing functions for that graphic depending on the characteristics of the video device intersecting your window's drawing region, the aim being to optimise the appearance of the image regardless of whether it is being displayed on, say, a grayscale device or a colour device. Recall from Chapter 11 - QuickDraw Preliminaries that when QuickDraw displays a colour on a grayscale screen, it computes the luminance, or intensity of light, of the desired colour and uses that value to determine the appropriate gray value to draw. It is thus possible that, for example, two overlapping objects drawn in two quite different colours on a colour screen may appear in the same shade of gray on a grayscale screen. In order for the user to differentiate between these two objects on a grayscale screen, you would need to provide an alternative drawing function which draws the two objects in different shades of gray on grayscale screens.

  • Window Zooming. The second issue is window zooming. For example, if the user drags a window currently zoomed to the user state so that it spans two screens, and then clicks the zoom box to zoom the window to the standard state, your application will need to determine which screen contains the largest area of the window, calculate the standard state for that screen (which will depend, amongst other things, on whether that screen contains the menu bar), and finally zoom the window out to the standard state for that particular screen.

  • Window Dragging and Sizing. In window dragging operations in a single-monitor environment, &qd.screenBits.bounds is typically passed in the limitRect parameter of DragWindow. (bounds is a rectangle which encloses the main screen.) Similarly, in window sizing operations in a single-monitor environment, the values in the bottom and right fields of bounds are typically assigned to the bottom and right fields of the rectangle passed in the sizeRect parameter of GrowWindow. For a multi-monitor environment, you should use the rectangle in the rgnBBox field of the Region structure filled in by a call to LMGetGrayRgn. This rectangle bounds the current desktop region, which spans multiple monitors.

Image Optimisation

The QuickDraw function DeviceLoop is central to the matter of optimising the appearance of your images. DeviceLoop searches for graphics devices which intersect your window's drawing region, informing your application of each graphics device it finds and providing your application with information about the current device's attributes. Armed with this information, your application can then invoke whichever of its drawing functions is optimised for those particular attributes.

DeviceLoop's second parameter is a pointer to an application-defined function. That function must be defined like this:

     pascal void myDrawingFunction(short depth,short deviceFlags,GDHandle targetDevice,
                                   long userData)

DeviceLoop calls this function for each dissimilar video device it finds. If it encounters similar devices (that is, devices having the same pixel depth, colour table seeds, etc.) it will make only one call to myDrawingFunction, pointing to the first such device encountered. DeviceLoop's behaviour can, however, be modified by supplying the flags parameter with one of the following values:

Value Meaning
singleDevices Do not group similar devices when calling drawing function.
dontMatchSeeds Do not consider ctSeed fields of ColorTable structures for graphics devices when comparing them.
allDevices Ignore value of drawingRgn parameter and instead call drawing function for every screen.

Window Zooming

Handling window zooming in a multi-monitors environment requires that your application provide a special application-defined function. The user may have moved a window to a different screen, or to a position where it spans two separate screens, since it was last zoomed. When the user elects to zoom that window to the standard state, your application-defined function must first determine the screen on which the zoomed window is to appear and the appropriate standard state for that screen.

See Chapter 4 - Windows for a description of standard state, user state, and the state data record.

The screen on which the zoomed window should appear should be the screen on which the window is currently displayed or, if the window spans screens, the screen containing the largest area of the window. The appropriate standard state will depend on:

  • The device's global boundaries, as contained in the gdRect field of the gDevice structure.

  • The requirements of the application. (As stated at Chapter 4 - Windows, the standard state on the main screen is typically the gray area of the screen minus three pixels all round.)

  • Whether the screen on which the zoomed window is to appear contains the menu bar.
After determining the screen on which the zoomed window is to appear and calculating the standard state, your application-defined function should call ZoomWindow to redraw the window frame in its new location and, finally, redraw the window's content region.

Vertical Blanking (VBL) Tasks

VBL Tasks and the Vertical Retrace Manager

The video circuitry in a Macintosh refreshes the screen at regular intervals, the exact interval depending on the video hardware. To refresh the screen, the monitor's electron beam draws in horizontal lines, starting at the upper left corner, finishing at the lower right corner, and then jumping back to the upper left corner. When the electron beam returns from the lower right corner to the upper left corner, the video circuitry generates a vertical retrace interrupt or vertical blanking (VBL) interrupt.

The Vertical Retrace Manager schedules tasks, known as VBL tasks, for execution during the vertical retrace interrupt. The Operating System itself uses the Vertical Retrace Manager to perform certain housekeeping operations, such as updating the global variable Ticks and the position of the cursor (every interrupt) and checking whether a disk has been inserted (every 30 interrupts).

You can also use the Vertical Retrace Manager to install your own recurrent tasks which, for some reason, you do not want to execute in your main event loop. Be aware, however, that:

  • The Vertical Retrace Manager is useful only for small, repetitive tasks which do not allocate or release memory.

  • The Vertical Retrace Manager is not an absolute timing device. Its operations are always relative to the VBL interrupt, which is sometimes disabled - for example, during disk access. (This latter explains the jerky cursor movement experienced during disk operations.)
VBL tasks installed by the Operating System are not maintained in the same queue as that used by application-defined VBL tasks.

Types of VBL Tasks

There are two general types of VBL tasks:

  • Slot-Based VBL Tasks. Slot-based VBL tasks are linked to an external video monitor. Because different monitors have different refresh rates, and hence execute VBL tasks at different intervals, a separate task queue is maintained for each attached video device. When a VBL interrupt occurs for one of these devices, the tasks in the queue relating to the slot holding that device's video card are executed. A slot-based VBL task is installed using SlotVInstall.

  • System-Based VBL Tasks. System-based VBL tasks apply to Macintoshes which have only a built-in monitor. On such machines, there is no need to isolate VBL tasks into separate queues. System-based VBL tasks are installed using VInstall.
To maintain compatibility on modular Macintoshes for software which uses VInstall, the Operating System generates a special interrupt at a frequency identical to the retrace rate on compact Macintoshes. This ensures that application tasks installed using the VInstall function, as well as the periodic system tasks previously described, are performed as usual.

VBL Task Rules

A VBL task which violates any of the following rules may cause a system crash:

  • A VBL task must not allocate, move, or purge memory, or call any Toolbox functions which may do so.

  • Applicable to 680x0 code only, a VBL task cannot call a function from any other code segment (see Code Segmentation, below) unless it sets up the application's A5 world properly. In addition, that segment must already be loaded in memory.

  • A VBL task cannot access your application's global variables unless it sets up the application's A5 world properly.

  • A VBL task's code, and any data accessed during the execution of the task, must be locked into physical memory if virtual memory is in operation.

VBL Tasks and Foreground/Background Switching

Some VBL tasks may be intended to perform services which are useful only to the application, and which should therefore cease execution if the application is switched to the background. Others may be intended to continue to execute even when the application is no longer in the foreground.

System-Based VBL Tasks

If the address of a system-based VBL task (not the same thing as the address of the VBL task structure) is anywhere in the partition of the application that installed it, the Process Manager automatically disables that task when it is sent to the background. Then, when the application regains control of the processor (through either a minor or major switch), the task is re-enabled. This does not apply if the address of a system-based VBL task is in the system partition.

You load a system-based task's VBL task record into the system partition when you want the task to be a persistent VBL task, that is, a task that continues to be executed even when the application which installed it is no longer in control of the CPU. (Note that slot-based VBLs are always persistent no matter where you put the task record.)

Note that, in the case of the address of the system-based task being in the application's partition, the task is re-enabled when the application receives processing time, which can occur without the application necessarily returning to foreground. For that reason, you may want to disable a system-based VBL task manually. This can be done using the same procedure as that applying to the disabling of a slot-based VBL task (see below).

Slot-Based VBL Tasks

By contrast, the Process Manager never disables a slot-based VBL task, no matter where the task is located. Accordingly, if you want a slot-based VBL task to be disabled when your application is in the background, you must do it yourself, either by removing the task structure from the VBL queue or by setting the vblCount field of the task structure (see below) to 0. You can do this in response to a suspend event. Then, when your application receives a resume event, you can re-enable the task by re-installing the task structure or by re-setting the vblCount field of the VBL task structure (see below) to the appropriate value.

Installing and Removing a VBL Task

You use the Vertical Retrace Manager to install and remove VBL task structures in and from system-based or slot-based vertical retrace queues. Before you call VInstall or SlotVInstall to install a task structure, you must first fill in the last four of the VBL task structure's fields.

The VBL Task Structure

The VBL task structure is defined by the VBLTask data type:

     struct VBLTask
       QElemPtr qLink;
       short    qType;
       VBLUPP   vblAddr;
       short    vblCount;
       short    vblPhase;
     typedef struct VBLTask VBLTask;
     typedef VBLTask *VBLTaskPtr;

Field Descriptions

qLink Pointer to the next entry in the queue. (This field is not set by the application. It is set by the Vertical Retrace Manager.)
qType The queue type. This must be set to vType.
vblAddr Pointer to the function that the Vertical Retrace Manager is to execute.
vblCount The number of interrupts before the function first executes.

The Vertical Retrace Manager lowers this number by 1 during each interrupt. If the value in vblCount is 0, the task will not execute. If, when vblCount contains 0, you want the function to be executed again, you must reset the vblCount field to the required value.

Setting this field to 0 is one way of disabling a task. A more common approach is to remove the VBL task structure from its queue by calling VRemove or SlotVRemove, although this should not be done by the task itself.

vblPhase The phase count of the VBL task.

In most cases, you can set this field to 0. However, if you install multiple tasks with the same vblCount at the same time, you can assign them different vblPhase values so that the tasks are not executed during the same interrupt. The value in the vblPhase field must be less than the value in the vblCount field.

Installing a VBL Task

For any particular VBL task, you must first decide whether to install it as a system-based VBL task or as a slot-based VBL task. The following considerations apply:

  • Slot-Based VBL Tasks. You need to install a task as a slot-based VBL task only if the execution of the task needs to be synchronised with the retrace rate of a particular external monitor. This will be the case, for example, if you want the repetitive re-drawing of a moving image to occur only during that particular monitor's vertical blanking period.

  • System-Based VBL Tasks. If the task performs no processing likely to affect the appearance of the screen, and no processing that depends on the state of an external monitor, you can install it as a system-based VBL task.
The next steps are to define the VBL task itself (so as be able to assign its address to the vblAddr field of the VBL task structure) and, in the case of slot-based VBL tasks, call LMGetMainDevice and GetDCtlEntry to find the slot number of the video device to whose retrace the VBL task is to be synchronised. The final step is to fill in a VBL task structure and install it into the appropriate queue.

VBL Task Structures Access - 680x0 Code

Recall that, if a VBL task is to be executed recurrently, it must reset the vblCount field of the VBL task structure each time it is executed. A repetitive VBL task must therefore be able to access its VBL task structure so that it can reset the vblCount field.

When the Vertical Retrace Manager executes the VBL task in a 680x0 environment, it places the address of the VBL task into the A0 register. The following defines an in-line function which moves that value onto the stack:

     pascal SInt32  GetVBLRec(void) = 0x2E88;

This in-line function, which returns a long integer specifying the address of the VBL task structure, should be called only from a VBL task. It will not work if called from the main program. In addition, the call should be the first line of your VBL task, because other processing could change the value in A0.

VBL Task Structure Access - PowerPC Code

In the PowerPC environment, the address of the VBL task structure is passed to the to the VBL task as an explicit parameter.

Accessing Application Global Variables - 680x0 Code

Recall from Chapter 1 that the boundary between the current application's global variables and its application parameters are stored in the 680x0 microprocessor's A5 register. Since all 680x0 applications share this register, the Process Manager keeps track of the address of your application's A5 world when a major or minor switch yields control of the microprocessor to another application. Then, when your application regains access to the CPU, the Process Manager restores that address to the A5 register.

Because VBL tasks are interrupt functions, they could well execute when the value in the A5 register does not point to your application's A5 world. As a result, if you need to access your application's global variables in a VBL task, you need to set the A5 register to its correct value when your VBL task begins executing and restore the previous value upon exit.

To achieve this, your 680x0 application should save its A5 using SetCurrentA5. Then, at interrupt time, the VBL task can begin by calling SetA5 to, firstly, set the A5 register to this saved value and, secondly, save the value that was in the A5 register immediately prior to the call. The VBL task should end with another call to SetA5, this time to restore the initial value.

The only memory location that a VBL task has access to is the address of the VBL task structure. Accordingly, if your application stores its A5 directly following the VBL task structure, it can locate this value by first locating the VBL task structure. To store the A5 value directly following the VBL task structure, define a new data type whose first field contains the VBL task structure and whose second field will hold the value in the A5 register retrieved by a call to SetCurrentA5:

     typedef struct
       VBLTask vblTaskStruc;  // The VBL task structure.
       long    vblA5          // Saved value of A5.
     } VBLStructure, *VBLStructurePtr;

You can think of this new data type as an expanded VBL task structure.

Accessing Application Global Variables - PowerPC Code

Setting and restoring the A5 register has no relevance in PowerPC code. In the PowerPC environment, the table of contents register always points to the table of contents for the currently executing code, through which the application's global variables can be addressed. As a result, your application's global variables are transparently available to any code compiled into your application.

Ensuring Compatibility with the Operating Environment

If your application is to run successfully in the software and hardware environments that may be present in a wide range of Macintosh models, it must be able to acquire information about a number of machine-dependent features and, where appropriate, act on that information.

Getting Operating Environment Information - The Gestalt Function

The Gestalt function may be used to acquire a wide range of information about the operating environment.

     OSErr  Gestalt(OSType selector,long *response);

     selector   Selector code.

     response   4-byte return result which provides the requested information.  When all
                four bytes are not needed, the result is expressed in the low-order byte.

     Returns:    Error code.  (0 = no error.)

The types of information capable of being retrieved by Gestalt are as follows:

  • The type of machine.

  • The version of the System file currently running.

  • The type of CPU.

  • The type of keyboard attached to the machine.

  • The type of floating-point unit (FPU) installed, if any.

  • The type of memory management unit (MMU).

  • The size of the available RAM.

  • The amount of available virtual memory.

  • The versions and features of various drivers and managers.

Gestalt Selectors

To use Gestalt, you pass it a selector, which specifies exactly what information your application is seeking. Of those selectors which are pre-defined by the Gestalt Manager, there are two sub-types:

  • Environmental Selectors. Environmental selectors are those which return information about the existence, or otherwise, of a feature. This information can be used by your application to guide its actions. Some examples of the many available environmental selectors, and the information returned in the reponse parameter, are as follows:

    Selector Information Returned
    gestaltFPUType FPU type.
    gestaltKeyboardType Keyboard type.
    gestaltLogicalRAMSize Logical RAM size.
    gestaltPhysicalRAMSize Physical RAM size.
    gestaltQuickdrawVersion QuickDraw version.
    gestaltTextEditVersion TextEdit version.

  • Informational Selectors. Informational selectors are those which provide information which should be used for the user's enlightenment only. This information should never be used as proof positive of some feature's existence, nor should it be used to guide your application's actions. Some example of informational selectors, and the information they return, are as follows:

    Selector Information Returned
    gestaltMachineType Machine type.
    gestaltROMVersion ROM version.
    gestaltSystemVersion System file version.

Gestalt Responses

In almost all cases, the last few characters in the selector's name form a suffix which indicates the type of value that will be returned in the response parameter. The following shows the meaningful suffixes:

Suffix Returned Value
Attr A range of 32 bits, the meaning of which must be determined by comparison with a list of constants.
Count A number indicating how many of the indicated type of items exist.
Size A size, usually in bytes.
Table Base address of table.
Type An index describing a particular type of feature.
Version A version number. Implied decimal points may separate digits of the returned value. For example, a value of 0x0750 returned in response to the gestaltSystemVersion selector means that system software version 7.5.0 is present.

Using Gestalt - Examples

The header file Gestalt.h defines and describes Gestalt Manager selectors, together with the many constants which may be used to test the response parameter.

Example 1

For example, when Gestalt is used to check whether Version 1.3 or later of Color QuickDraw is present, the value returned in the response parameter may be compared with gestalt32BitQD13 as follows:

     OSErr    osErr
     SInt32   response;
     Boolean  colorQuickDrawVers13Present = true;

     osErr = Gestalt(gestaltQuickdrawVersion,&response);
     if(osErr == noErr)
       if(response < gestalt32BitQD13)
         colorQuickDrawVers13Present = false;

Example 2

Many constants in Gestalt.h represent bit numbers. In this example, the value returned in the response parameter is tested to determine whether bit number 5 (gestaltHasSoundInputDevice) is set:

     OSErr   osErr;
     SInt32  response;
     Boolean hasSoundInputDevice = false;

     osErr = Gestalt(gestaltSoundAttr,&response);
     if(osErr == noErr)
       gHasSoundInputDevice = BitTst(&response,31 - gestaltHasSoundInputDevice);

Note that the function BitTst is used to determine whether the specified bit is set. Bit numbering with BitTst is the opposite of the usual MC680x0 numbering scheme used by Gestalt. Thus the bit to be tested must be subtracted from 31. This is illustrated in the following:

  Bit numbering as used in BitTst
  ...  7  8  9  10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
  Bit as numbered in MC69000 CPU operations, and used by Gestalt
  ...  8  7  6  5  4  3  2  1  0  15 14 13 12 11 10 9  8  7  6  5  4  3  2  1  0

  gestaltHasSoundInputDevice = 5
  31 - 5 = 26

Code Segmentation and Heap Space Optimisation - 680x0 Code

680x0 Macintosh programs may be divided into several segments. The Macintosh system software limits segments to 32K; accordingly, if you are writing a large program, you must segment your code.

Observing the 32K limit is, however, not the only reason for segmenting your 680x0 code. Segments equate, in the built application, to units of executable code which are stored in resources of type 'CODE' and which are loaded into your application's heap as relocatable blocks. Because these resources are loaded into memory only when required, and because your application can cause them to be marked as purgeable when no longer needed, segmentation allows you to optimise your 680x0 application's heap space. Put another way, segmentation allows you to provide the user with the maximum possible heap space to accommodate the windows and user data, etc., created while the 680x0 application is running.

The main segment (that is, the segment containing the main function) is loaded and locked by the system when the application is launched. Thereafter, when the application makes a call to a function in one of the remaining segments, the Segment Loader, with no help from the application, automatically loads that segment, moves it high in the application's heap, locks it, and passes control to the called function.

Ultimately, of course, all code segments will be brought into memory and locked, creating the same memory-hogging situation as would obtain if the application had not been segmented. To prevent that situation, your application should, at the appropriate time, unlock these blocks and make them purgeable. Note that this applies to all but the main code segment, which must never be unlocked or made purgeable. The following describes an appropriate methodology for unlocking and marking as purgeable the other code segments of your application:

  • Create a new stub, or "do nothing" function, for each of the code segments you want to unload. For example, this is a stub for a code segment called updateSegment:

         void  updateSegment(void) {}

  • Include each stub in its associated code segment.

  • Write a function called, say, doUnloadSegments which calls the Segment Loader function UnloadSeg for each of the stubs. The following is an example:

         void  doUnloadSegments(void)
           // Other UnloadSeg calls here as required.
    Note that each UnloadSeg call looks up the code segment that contains the stub function in its input parameter, unlocks that segment, and makes it purgeable. Note also that you could pass any of the segment's functions as the parameter to the UnloadSeg call; however, it is preferable to use stubs dedicated to this purpose because the other functions in the segment could well be moved to another segment during future updating of the code.

  • Place the doUnloadSegments function in the main code segment and call it at the bottom of the main event loop (which should also be located in the main code segment) so that all code segments specified in the function will be unlocked and marked as purgeable after a received event has been handled to completion. The following is an example:

         void  main(void)
One or more of the unlocked and purgeable code segments may then be purged by the Memory Manager if this becomes necessary in order to satisfy a memory allocation request. When a call is subsequently made to a function contained in one of the purged segments, the Segment Loader once again loads that segment into your application's heap as a relocatable block.

PowerPC Considerations

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 Managers's UnloadSeg function are simply ignored.

Main Notification Manager Data Types and Functions

Data Types

Notification Structure

struct NMRec
  QElemPtr   qLink;       // Next queue entry. 
  short      qType;       // Queue type. 
  short      nmFlags;     // (Reserved.) 
  long       nmPrivate;   // (Reserved.) 
  short      nmReserved;  // (Reserved.) 
  short      nmMark;      // Item to mark in Apple menu. 
  Handle     nmIcon;      // Handle to small icon. 
  Handle     nmSound;     // Handle to sound structure. 
  StringPtr  nmStr;       // String to appear in alert. 
  NMUPP      nmResp;      // Pointer to response function. 
  long       nmRefCon;    // For application use. 
typedef struct NMRec NMRec;
typedef NMRec *NMRecPtr;


Add Notification Request to the Notification Queue

OSErr  NMInstall(NMRecPtr nmReqPtr);

Remove Notification Request from the Notification Queue

OSErr  NMRemove(NMRecPtr nmReqPtr); 

Relevant Process Manager Data Types and Functions

Data Types

Process Serial Number

struct ProcessSerialNumber
  unsigned long  highLongOfPSN;
  unsigned long  lowLongOfPSN;


Get Process Serial Number of a Particular Process

OSErr  GetCurrentProcess(ProcessSerialNumber *PSN);

Get Process Serial Number of Foreground Process

OSErr  GetFrontProcess(ProcessSerialNumber *PSN);

Compare Two Process Serial Numbers

OSErr  SameProcess(const ProcessSerialNumber *PSN1,const ProcessSerialNumber *PSN2,
       Boolean *result);

Relevant Event Manager Data Types and Functions

Data Types

QHdr (Defines the Queue Header)

struct QHdr
  short     qFlags;
  QElemPtr  qHead;
  QElemPtr  qTail;
typedef struct QHdr QHdr;
typedef QHdr *QHdrPtr;


struct QElem
  QElemPtr  qLink;
  short     qType;
  short     qData[1];
typedef struct QElem QElem;
typedef QElem *QElemPtr;

EvQEl (Defines an Entry in the Operating System Event Queue)

struct EvQEl 
  QElemPtr       qLink;
  short          qType;
  EventKind      evtQWhat;
  Uint32         evtQMessage;
  Uint32         evtQWhen;
  Point          evtQWhere;
  EventModifiers evtQModifiers;
typedef struct EvQEl EvQEl;
typedef EvQEl *EvQElPtr;


Get Address of Event Queue Header

QHdrPtr  LMGetEventQueue(void);

Relevant Color Picker Utilities Function

Boolean  GetColor(Point where,ConstStr255Param prompt,const RGBColor *inColor,
         RGBColor *outColor)

Relevant QuickDraw Constants and Functions


Flag Bits for gdFlags Field of GDevice Structure

mainScreen    = 11  // Graphics device is main screen.
screenDevice  = 13  // Graphics device is a screen device.
screenActive  = 15  // Graphics device is current device.


Getting Available Graphics Devices

GDHandle  LMGetDeviceList(void);
GDHandle  LMGetMainDevice(void);
GDHandle  GetNextDevice(void);

Determining the Characteristics of a Video Device
void     DeviceLoop(RgnHandle drawingRgn,DeviceLoopDrawingUP drawingProc,
         long userData,DeviceLoopFlags flags);
Boolean  TestDeviceAttribute(GDHandle gdh,short attribute);

Getting the Intersection Between Two Rectangles and Determining the Overlap

Boolean  SectRect(Rect rect1,Rect rect2,Rect resultRect);
Vertical Retrace Manager Data Types and Functions

Data Types

VBL Task Structure

struct VBLTask
  QElemPtr  qLink;
  short     qType;
  VBLUPP    vblAddr;
  short     vblCount;
  short     vblPhase;
typedef struct VBLTask VBLTask,*VBLTaskPtr;


Slot-Based Installation and Removal Routines

OSErr  SlotVInstall(QElemPtr vblBlockPtr,short theSlot);
OSErr  SlotVRemove(QElemPtr vblBlockPtr,short theSlot);

System-Based Installation and Removal Routines

OSErr  VInstall(QElemPtr vblTaskPtr);
OSErr  VRemove(QElemPtr vblTaskPtr);

Utility Routines

OSErr    AttachVBL(short theSlot);
OSErr    DoVBLTask(short theSlot);
QHdrPtr  GetVBLQHdr(void);

Relevant Gestalt Manager Function

OSErr  Gestalt(OSType selector,long *response);

Relevant Segment Loader Functions

Unlock Code Segments and Make Purgeable

void  UnloadSeg(void * routineAddr);

Terminate Caller, Release Heap, and Launch Finder

void ExitToShell(void); 

Go to Demo


Community Search:
MacTech Search:

Software Updates via MacUpdate

OmniOutliner Pro 5.4.1 - Pro version of...
OmniOutliner Pro is a flexible program for creating, collecting, and organizing information. Give your creativity a kick start by using an application that's actually designed to help you think. It's... Read more
EarthDesk 7.3 - $24.99
EarthDesk replaces your static desktop picture with a rendered image of Earth showing correct sun, moon, and city illumination. With an Internet connection, EarthDesk displays near-real-time global... Read more
Monosnap 3.5.3 - Versatile screenshot ut...
Monosnap lets you capture screenshots, share files, and record video and .gifs! Features Capture Capture full screen, just part of the screen, or a selected window Make your crop area pixel... Read more
Monosnap 3.5.3 - Versatile screenshot ut...
Monosnap lets you capture screenshots, share files, and record video and .gifs! Features Capture Capture full screen, just part of the screen, or a selected window Make your crop area pixel... Read more
Spotify - Stream music, creat...
Spotify is a streaming music service that gives you on-demand access to millions of songs. Whether you like driving rock, silky R&B, or grandiose classical music, Spotify's massive catalogue puts... Read more
Evernote 7.6 - Create searchable notes a...
Evernote allows you to easily capture information in any environment using whatever device or platform you find most convenient, and makes this information accessible and searchable at anytime, from... Read more
Final Cut Pro X 10.4.4 - Professional vi...
Final Cut Pro X is a professional video editing solution. Completely redesigned from the ground up, Final Cut Pro adds extraordinary speed, quality, and flexibility to every part of the post-... Read more
Compressor 4.4.2 - Adds power and flexib...
Compressor adds power and flexibility to Final Cut Pro X export. Customize output settings, work faster with distributed encoding, and tap into a comprehensive set of delivery features. Features... Read more
Motion 5.4.2 - Create and customize Fina...
Motion is designed for video editors, Motion 5 lets you customize Final Cut Pro titles, transitions, and effects. Or create your own dazzling animations in 2D or 3D space, with real-time feedback as... Read more
Thunderbird 60.3.1 - Email client from M...
As of July 2012, Thunderbird has transitioned to a new governance model, with new features being developed by the broader free software and open source community, and security fixes and improvements... Read more

Latest Forum Discussions

See All

Slots Panther Vegas offers a social gamb...
New era of Online gambling Want to try your luck in online social gambling? More and more people are into online casinos as a risk-free amazing way to experience the excitement of a big game. Online casinos and slots machines are gaining popularity... | Read more »
3 features we think you'll love in...
Well known classic RPG “Shin Megami Tensei” franchise originally created by Atlus, can now be played throughout iOS and Android. Created by Sega, “Shin Megami Tensei” has spawned a mobile-centric installment in the shape of “Shin Megami Tensei:... | Read more »
These are the top 3 games for iPhone and...
The end of the week has rolled around again, which means it's time for us to look forward to the games you're going to be playing over the next seven days. We've got the return of a mobile gaming legend next week, as well as a couple of other... | Read more »
Time for you to pick which of these top...
Oh look, Thursday is upon us once more. And we all know what that means! You guessed it, it's time for you to vote for which of these five games you think deserves to win our game of the week award. And have we got a selection for y'all this week... | Read more »
Dragalia Lost - High Midgardsormr Prep G...
It might not seem like there's a ton to do between events in Dragalia Lost, but there is one high level piece of content that can keep you occupied for a long time. Defeating High Midgardsormr is currently the game's most difficult non-event... | Read more »
Get your friends, these are the top 5 be...
You can't be a lone wolf all the time, especially if you want to show off your gaming prowess. And that's where this list comes in - we're running down what we think are the top 5 multiplayer games for iPhone. There might be some controversial... | Read more »
SpitKiss is the worthy winner of last we...
It's been a rough and tumble battle this week, with all of the games managing to get a few hits in where it counts, but after checking with the independent adjudicators at ringside, we can now reveal that gloriously gross smooching sim SpitKiss has... | Read more »
The best games for iPhone - The definiti...
Hi there, and welcome to our ever-increasing list of the very best games for iPhone. We're going to be updating this regularly with new content, so make sure you check back often, because you're not going to want to miss out on even one of the... | Read more »
Dragalia Lost Guide - What You Need To K...
Another raid has come and gone in Dragalia Lost, but that doesn’t mean there’s not still lots to do. In fact, the game’s next event, A Wish to the Winds, has already been announced and will be coming to the game this Wednesday. Although details are... | Read more »
The top 5 best games like Star Wars: Gal...
One of the things we like to do here at 148Apps is broaden your horizons. Maybe you're a fan of Star Wars: Galaxy of Heroes and you're looking for something that's going to scratch similar itches? Well that's where we come, and more specifically... | Read more »

Price Scanner via

Save on a new MacBook with these early Black...
B&H Photo has posted early Black Friday sale prices on Apple MacBooks, including up to $300 off MSRP on 15″ MacBook Pros, $100 off new 13″ MacBook Airs, and more. Most of these deals expire... Read more
T-Mobile Black Friday deal: Free iPhone Xr wi...
T-Mobile is offering the 64GB iPhone Xr for free as part of their Black Friday 2018 sale. Two new lines are required, as well as an eligible trade-in (iPhone 6s models or newer). $20.84 is applied to... Read more
Save up to $157 on a 10.5″ iPad Pro with thes...
Apple’s newest authorized reseller, Jet, has 10.5″ iPad Pros on sale for up to $157 off MSRP as part of their Black Friday week sale. Shipping is free. Note that some sale prices may be restricted to... Read more
US Cellular offers free iPhone Xr for new lin...
US Cellular is offering the 64GB iPhone Xr for free as part of their Black Friday 2018 sale. A new line is required, but there is no trade in requirement. Any of the iPhone Xr colors qualify. The... Read more
Roundup of Black Friday Week 2018 Sales &...
At, we give you the most accurate Mac and Apple prices on the web. Choose one of our price trackers at the top of the page to see all the current sale prices on Apple’s products from... Read more
Details of Amazon’s 2018 Black Friday week di...
Amazon’s recent agreement with Apple has allowed the online store to add most of Apple’s most popular products to its inventory, including new 2018 iPad Pros, Mac minis, Apple Watch Series 4, and... Read more
Get A Job With Apple, The No. 3 ‘World’s Best...
FEATURE: 11.16.18- If you are a fan of Apple, Inc. and an avid user of any one of its vast array of tech gadgets, why not take it a step further and work for the company behind those products you... Read more
Sprint offers $100 discounts on Apple Watch S...
Sprint is offering customers $100 discounts on the purchase of a cellular Apple Watch Series 4 or Apple Watch Series 3. Their discount reduces the cost of a Series 4 watch to $399 (40mm) or $429 (... Read more
New 2018 11″ 64GB & 256GB iPad Pros in st...
MacMall has the new 2018 11″ 64GB and 256GB iPad Pros in stock today for $50 off Apple’s prices. They’re currently the lowest prices available for these new iPad Pros. – 11″ 64GB Space Gray WiFi iPad... Read more
New Mac minis in stock and available today at...
Apple Authorized Reseller Adorama has the new 2018 Mac minis in stock today today sales tax free for residents outside of NY & NJ. Shipping is also free. – 3.6GHz Quad-Core mini: $799 – 3.0GHz 6... Read more

Jobs Board

Best Buy *Apple* Computing Master - Best Bu...
**658102BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Sales **Location Number:** 000395-Pensacola-Store **Job Description:** **What does a Read more
*Apple* Mobile Master - Best Buy (United Sta...
**658022BR** **Job Title:** Apple Mobile Master **Job Category:** Store Associates **Location Number:** 000793-Dothan-Store **Job Description:** **What does a Best Read more
Geek Squad *Apple* Master Consultation Agen...
**657784BR** **Job Title:** Geek Squad Apple Master Consultation Agent **Job Category:** Services/Installation/Repair **Location Number:** 000597-Erie-Store **Job Read more
Best Buy *Apple* Computing Master - Best Bu...
**655276BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Sales **Location Number:** 000387-Randall Road-Store **Job Description:** **What does a Read more
Omni-Channel Associate - *Apple* Blossom Ma...
Omni-Channel Associate - Apple Blossom Mall Location:Winchester, VA, United States- Apple Blossom Mall 1850 Apple Blossom Dr Job ID:1074107 Date:November 12, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.