Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: More Macintosh Toolbox /
Chapter 8 - Control Panels / Creating an Extension for the Monitors Control Panel


Writing a Monitors Extension Function

You create a monitors extension function to implement the feature for your video card and manage the controls that allow the user to set values for that feature. The Monitors control panel calls your monitors extension function, requesting it to perform an action or handle an event in response to the user's manipulation of the controls for your video card. The message parameter identifies the action or event.

Your monitors extension function should perform the requested action and return a function result to the Monitors control panel. This function result should be either a standard value indicating that your monitors extension function has not allocated memory, a handle to any memory you allocate, or an error code. Here is how you declare a monitors extension function:

FUNCTION MyMntrExt (message, item, numItems: Integer; monitorValue: LongInt;
                    mDialog: DialogPtr; theEvent: EventRecord;
                    screenNum: Integer; VAR screens: ScrnRsrcHandle;
                    VAR scrnChanged: Boolean): LongInt; 
The message parameter can contain any of the values defined by these constants:

CONST
   startupMsg     = 12; {status of user (whether a superuser)}
   initMsg        = 1;  {perform initialization}
   okMsg          = 2;  {user clicked OK button}
   cancelMsg      = 3;  {user clicked Cancel button}
   hitMsg         = 4;  {user clicked enabled control}
   nulMsg         = 5;  {null event}
   keyEvtMsg      = 9;  {keyboard event}
   updateMsg      = 6;  {update event}
The value of the message parameter indicates the action your monitors extension function should perform:

In addition, the message parameter can contain any of the values defined by these constants:

CONST
   activateMsg    = 7;  {becoming active (not currently used)}
   deactivateMsg  = 8;  {becoming inactive (not currently used)}
   superMsg       = 10; {user is a superuser}
   normalMsg      = 11; {user is not a superuser}
These messages either are provided for backward compatibility or are not currently used:

IMPORTANT
If your monitors extension function cannot handle a message, it should return as its function result a handle to any memory it previously allocated. Otherwise, it should return the value passed in the monitorValue parameter.
For a description of the remaining parameters of the monitors extension function, see "Monitors Extension Functions" beginning on page 8-79.

Your monitors extension function can return either an error code or a handle to memory it allocated. Each time the Monitors control panel calls your monitors extension function, the monitorValue parameter contains the value that your function returned as its function result the last time it was called.

If an error occurs, your monitors extension function should display an error dialog box and then return a value between 1 and 255. If your function returns a value in this range, the Monitors control panel closes the Options dialog box immediately and does not call your monitors extension function again.

The monitors extension used as an example in this chapter adds controls to the Options dialog box for a video card called SurfBoard. The Magnify Enabled checkbox allows the user to magnify the display of text and graphics on the monitor connected to the SurfBoard video card. The SurfBoard monitors extension also includes controls for superusers, which illustrate how to implement the rectangle extension in which
the superuser controls are displayed. The SurfBoard monitors extension shows one way of handling messages from the Monitors control panel.

Listing 8-25 shows the SurfBoard monitors extension function, MyMonExtend. It includes a CASE statement that handles messages that the Monitors control panel passes to MyMonExtend. First the function sets up a handle for memory that it allocates in response to the startup message. The function returns a handle to the storage it allocates as its function result in response to the startup message, unless an error occurs (see Listing 8-26 on page 8-66). For all subsequent messages, the Monitors control panel passes, in the monitorValue parameter, the previous function result. The MyMonExtend function returns the handle to the allocated memory as its function result for any messages that it does not handle.

Listing 8-25 A monitors extension function

UNIT SurfBoardMonExt;
INTERFACE
   {include a Uses statement if your programming environment requires it}
CONST
   kTextItem         = 1;        {static text item}
   kSuperUserDivLine = 2;        {separation line}
   kFilterControl    = 4;        {radio button filter}
   kAntiAliasingCntl = 5;        {radio button aliasing}
   kMagnifyControl   = 6;        {checkbox for Magnify Enabled}
   kMemErrAlert      = 130;      {resource ID of out-of-memory alert box}
   kdeepAlert        = 131;      {resource ID of alert box}
   kResID            = 133;      {all other errors}
TYPE
   MonitorDataRec = 
      RECORD                     {local data for the extension}
         isSuperUser:         Boolean; 
         filteringSetting:    Integer;
         oldFiltering:        Integer;
         toggleMagnifyValue:  Integer;
      END;
   MonitorDataPtr       = ^MonitorDataRec;
   MonitorDataHandle    = ^MonitorDataPtr;

   MyRectHandle = ^RectPtr;
   MyIntPtr     = ^Integer; 
   MyIntHandle  = ^MyIntPtr;
FUNCTION MyMonExtend (message, item, numItems: Integer; 
                      monitorValue: LongInt; mDialog: DialogPtr; 
                      theEvent: EventRecord; ScreenNum: Integer; 
                      VAR Screens: ScrnRsrcHandle; 
                      VAR ScrnChanged: Boolean): LongInt;
IMPLEMENTATION
{any support routines your monitors extension function uses}
PROCEDURE MyHandleStartupMsg(item: Integer; mDialog: DialogPtr; 
                            VAR monitorValue: LongInt); FORWARD;
PROCEDURE MyHandleInitMsg(numItems: Integer; mDialog: DialogPtr;
                          dataRecHand: MonitorDataHandle); FORWARD;
PROCEDURE MyDrawRect(theWindow: WindowPtr; itemNo: Integer); FORWARD;
FUNCTION MySetUpData (superUser: Integer; storage: MonitorDataHandle): OSErr;
                      FORWARD;
PROCEDURE MyHandleHits (mDialog: DialogPtr; whichItem, numItems: Integer;
                        dataRecHand: MonitorDataHandle); FORWARD;
PROCEDURE MySaveNewValues (dataRecHand: MonitorDataHandle); FORWARD;
PROCEDURE MyUndoChanges (item, numItems: Integer; mDialog: DialogPtr;
                         dataRecHand: MonitorDataHandle); FORWARD;
FUNCTION MyMonExtend (message, item, numItems: Integer; monitorValue: LongInt;
                      mDialog: DialogPtr; theEvent: EventRecord;
                      ScreenNum: Integer; VAR Screens: ScrnRsrcHandle;
                      VAR ScrnChanged: Boolean): LongInt; 
VAR
   dataRecHand:   MonitorDataHandle;
BEGIN
   IF message <> startupMsg THEN
      dataRecHand := MonitorDataHandle(monitorValue); {set up handle}
   CASE message OF
   startupMsg:    
      MyHandleStartupMsg(item, mDialog, monitorValue);
   initMsg: 
      MyHandleInitMsg(numItems, mDialog, dataRecHand);
   hitMsg:
      MyHandleHits(mDialog, item, numItems, dataRecHand);
   okMsg:   
      MySaveNewValues(dataRecHand);
   cancelMsg:  
      MyUndoChanges(item, numItems, mDialog, dataRecHand);
   END; {of CASE}
   MyMonExtend := monitorValue;{return value with handle}
END; {MyMonExtend} 

Handling the Startup Message

After the code in your monitors ('mntr') code resource is loaded and before the Monitors control panel finds any resources to which your monitors extension function refers, the Monitors control panel calls your function with a startup (startupMsg) message. If the user is a superuser, the Monitors control panel sets the item parameter to 1 for the startup message.

The startup message requests your monitors extension function to load and modify any resources that must allow for the capabilities of the computer or for superusers. For example, your monitors extension function should modify the rectangle resource if the user is a superuser.

In response to a startup message, your function can also create a handle and allocate any memory that it needs to store values between calls from the Monitors control panel. For example, if your function initializes its controls in response to the initialization (initMsg) message, it should store a value indicating whether or not the user is a superuser. When the Monitors control panel calls your monitors extension function with an initialization message, the item parameter no longer indicates the user's status. If your code allocates memory, your function should return as its function result a handle to the memory it allocates in response to the startup message, unless an error occurs. If an error occurs, your function can display an error dialog box and return a function result of 255, indicating an error condition. Listing 8-26 shows how the MyMonExtend function handles the startup message.

Listing 8-26 Handling the startup message

PROCEDURE MyHandleStartupMsg (item: Integer; mDialog: DialogPtr;
                              VAR monitorValue: LongInt);
VAR
   dataRecHand:   MonitorDataHandle;
   result:        OSErr;
   i:             Integer;
BEGIN
   {allocate memory to store data}
   dataRecHand := 
      MonitorDataHandle(NewHandle(sizeof(MonitorDataRec)));
   IF dataRecHand <> NIL THEN 
   BEGIN
      result := MySetUpData(item, dataRecHand);   
      IF result = noErr THEN
         monitorValue := LongInt(dataRecHand)
      ELSE  {error function result stops any further action}
         monitorValue := result;
   END
   ELSE
   BEGIN {dataRecHand not allocated}
      i := StopAlert(kMemErrAlert, NIL);
      {error function result stops any further action}
      monitorValue := 255;
   END;
END;
Allocating Storage in Response to the Initialization Message
If your monitors extension function does not allocate memory in response to a startup message, it can do so in response to an initialization message, and then use the superuser (superMsg) or the normal user (normalMsg) message to initialize control values and user items, if any. The Monitors control panel does not display the Options dialog box until after your monitors extension function returns from either of these messages.
If your function returns an error in response to the startup message, the Monitors control panel does not display the Options dialog box. Your code can display an alert box describing the error before returning control to the Monitors control panel.

After it allocates storage, the function shown in Listing 8-26 calls its own MySetUpData function to check the value of the item parameter. This value indicates whether the user has selected superuser status.

Listing 8-27 shows the MySetUpData function. If the user is not a superuser, the SurfBoard monitors extension uses the default values for the rectangle resource. (This rectangle ends just before the dividing line, so that the superuser controls are not displayed.) If the user is a superuser, MySetUpData extends the rectangle in the rectangle ('RECT') resource to include all of the controls in the item list resource ('DITL') resource. If an error occurs, the function notifies the user and returns an error code value of 255 as its function result.

Listing 8-27 Using a normal user rectangle or extending it to display superuser controls

FUNCTION MySetUpData(superUser: Integer; storage: MonitorDataHandle): OSErr;
VAR
   magnifyHdl:             Handle;
   intensityLevelHdl:      Handle;
   resHandle:              Handle;
   i:                      Integer;
   result:                 OSErr;
BEGIN
   result := noErr;           
   HLock(Handle(storage));
   WITH storage^^ DO
   BEGIN
      {open preferences file first if needed}
      magnifyHdl := GetResource('MAGN', kResID);
      IF magnifyHdl <> NIL THEN 
      BEGIN
         toggleMagnifyValue := MyIntHandle(magnifyHdl)^^;
         ReleaseResource(magnifyHdl);
      END;
      IF superUser = 1 THEN
      BEGIN
         isSuperUser := TRUE;
         intensityLevelHdl := GetResource('INTE', kResID);
         IF intensityLevelHdl <> NIL THEN
         BEGIN
            oldFiltering := MyIntHandle(intensityLevelHdl)^^;
            filteringSetting := oldFiltering;
            ReleaseResource(intensityLevelHdl);
            resHandle:= GetResource('RECT', -4096);
            IF resHandle <> NIL THEN
               RectHandle(resHandle)^^.top := -160
            ELSE
               result := 255
         END
         ELSE
            result := 255;
      END   {of superuser = 1}
      {close preferences file}
   END;  {of WITH}
   IF result = 255 THEN
   BEGIN
      DisposeHandle(Handle(storage));
      i := StopAlert(kdeepAlert, NIL);
   END;
   HUnlock(Handle(storage));
   MySetUpData := result;
END;

Performing Initialization

Before it displays the Options dialog box and after it has located any resources that your monitors extension includes, such as gamma table ('gama') resources, the Monitors control panel calls your monitors extension function with an initMsg message. When your monitors extension function receives this message, it should set default values for controls. To handle this message, your function can initialize the settings of its controls. If it hasn't already allocated memory in response to the startup message, your function can allocate memory when it performs initialization. The Monitors control panel calls your monitors extension with an initialization message after the startup message and before either the superuser or normal message.

If your function returns an error in response to the initMsg message, the Monitors control panel does not display the Options dialog box. Your function can display an alert box describing the error before returning control to the Monitors control panel.

Listing 8-28 shows the MyHandleInitMsg procedure, which the MyMonExtend function calls to handle the initialization message. First MyHandleInitMsg sets its controls to their initial values; MyHandleInitMsg calls the Dialog Manager's GetDialogItem and the Control Manager's SetControlValue procedures for this purpose. Then, if the user is a superuser, the procedure installs the procedure that draws the dividing line between the normal controls and superuser controls, then initializes the settings of its superuser controls.

Listing 8-28 Initializing a monitors extension

PROCEDURE MyHandleInitMsg (numItems: Integer; mDialog: DialogPtr;
                           dataRecHand: MonitorDataHandle);
VAR
   itemType:      Integer;
   itemHandle:    Handle;
   itemRect:      Rect;
BEGIN
   GetDialogItem(mDialog, numItems+kMagnifyControl, itemType, 
                 itemHandle, itemRect);
   SetControlValue(ControlHandle(itemHandle),
                   (dataRecHand^^.toggleMagnifyValue));
   IF dataRecHand^^.isSuperUser THEN
   BEGIN 
      GetDialogItem(mDialog, numItems+kSuperUserDivLine, itemType, 
                    itemHandle, itemRect);
      SetDialogItem(mDialog, numItems+kSuperUserDivLine, itemType, 
                    @MyDrawRect, itemRect);
      IF dataRecHand^^.oldFiltering = 0 THEN
         GetDialogItem(mDialog, numItems+kAntiAliasingCntl,
                       itemType, itemHandle, itemRect)
      ELSE
         GetDialogItem(mDialog, numItems+kFilterControl, 
                       itemType, itemHandle, itemRect);
      SetControlValue(ControlHandle(itemHandle), 1);
   END;
END;
Listing 8-29 shows the MyDrawRect procedure, which draws the line dividing superuser controls from other controls. The MyDrawRect procedure uses the FrameRect procedure to draw a 1-pixel-high rectangle. Note that MyDrawRect specifies the coordinates for the dividing line in the coordinate system used by its rectangle ('RECT') resource. If you wish, you can draw this line in a gray pattern so that it looks similar to the dividers in menus. (For information on the FrameRect procedure, see Inside Macintosh: Imaging with QuickDraw.)

Listing 8-29 Drawing a line to separate superuser controls

PROCEDURE MyDrawRect (theWindow: WindowPtr; itemNo: Integer);
VAR 
   itemType:      Integer;
   itemHdl:       Handle;
   itemRect:      Rect;
BEGIN
   GetDialogItem(theWindow, itemNo, itemType, itemHdl, itemRect);
   FrameRect(itemRect);
END;

Responding to a Click in the OK Button

The Monitors control panel calls your monitors extension function with an OK (okMsg) message when the user clicks the OK button. The OK button is a standard control defined for the Options dialog box by the Monitors control panel. When the user clicks the OK button, the Monitors control panel hides the Options dialog box.

This message is a signal to put user preferences into effect. You should not make any changes requested by the user irreversible until you receive this message. This is your last chance to check the values of any controls or editable text items that the user might have changed. Your monitors extension function should update the resources in which it saves values; it should also make any hardware changes necessary. Your function should release any memory it has allocated before returning control to the Monitors control panel.

The MyMonExtend function (see Listing 8-25 on page 8-64) calls its own MySaveNewValues procedure to handle an OK message from the Monitors control panel. This procedure checks if the user has changed the setting of the Magnify Enabled checkbox. If the user is a superuser, it also checks the values of the Anti-Aliasing and Zirconian Filtration radio buttons. If the user changed values, MyMonExtend writes the values to its preferences file, which is stored in the Preferences folder, and releases any memory it has allocated before it returns to the Monitors control panel.

Responding to a Cancel Request

When the user clicks the Cancel button, the Monitors control panel calls your monitors extension function with a cancel (cancelMsg) message. The Cancel button is a standard control defined for the Options dialog box by the Monitors control panel. To handle the cancel request, your monitors extension function should restore the system to its former state, before the user clicked the Options button; release any memory it allocated; and return control to the Monitors control panel. If your function modified any values the user specified before clicking the Cancel button, reinstate the original values.

Handling Mouse Events for a Monitors Extension

When the user clicks any active enabled control that your monitors extension defined for the Options dialog box, system software generates mouse events. The Monitors control panel intercepts these events and passes them to your monitors extension function as a hitMsg message. Your monitors extension function typically changes the setting of the control or performs the appropriate action in response to a hitMsg message.

Along with the hitMsg message, the Monitors control panel passes three values that your monitors extension function uses to determine which item the user clicked.

The Monitors control panel appends the items you define in your monitors extension item list to the item list for the standard controls in the Options dialog box. Therefore, to get the actual number of your item, subtract numItems from item.

Listing 8-30 shows the MyHandleHits procedure, which MyMonExtend calls to handle a hitMsg message. This procedure determines the item number of the clicked control, as defined in the monitors extension's item list resource. It does this by subtracting the number of items in the item list of the Options dialog box (numItems) from the item the user clicked (whichItem) to get the correct item number. Then MyHandleHits calls the Dialog Manager's GetDialogItem procedure and the Control Manager's SetControlValue procedure to set the control to the new value indicated by the user.

Listing 8-30 Responding when a user clicks a control

PROCEDURE MyHandleHits (mDialog: DialogPtr; whichItem, numItems: Integer;
                        dataRecHand: MonitorDataHandle);
VAR 
   itemType:   Integer;
   itemHandle: Handle;
   itemRect:   Rect;
BEGIN
   HLock(Handle(dataRecHand));
   WITH dataRecHand^^ DO
   BEGIN
      CASE whichItem - numItems OF
         kFilterControl:
            BEGIN
               GetDialogItem(mDialog, whichItem, itemType, itemHandle,
                             itemRect);
               SetControlValue(ControlHandle(itemHandle),1); 
               GetDialogItem(mDialog, numItems+kAntiAliasingCntl, itemType,
                            itemHandle, itemRect);
               SetControlValue(ControlHandle(itemHandle),0);
               filteringSetting := 1;
            END;
         kAntiAliasingCntl:
            BEGIN
               GetDialogItem(mDialog, numItems+kFilterControl, itemType,
                            itemHandle, itemRect);
               SetControlValue(ControlHandle(itemHandle),0);
               GetDialogItem(mDialog, whichItem, itemType, itemHandle,
                            itemRect);
               SetControlValue(ControlHandle(itemHandle),1);
               filteringSetting := 0;
            END;


         kMagnifyControl:
            BEGIN
               GetDialogItem(mDialog, whichItem, itemType, itemHandle,
                             itemRect);
               toggleMagnifyValue := 1 - toggleMagnifyValue;
               SetControlValue(ControlHandle(itemHandle),
                              toggleMagnifyValue);
            END;
      END; {end of CASE}
   END;
   HUnlock(Handle(dataRecHand));
END;

Handling Keyboard Events

The Monitors control panel intercepts all key-down and auto-key events for your monitors extension and sends your monitors extension function a keyboard event through the keyEvtMsg message. The Monitors control panel passes, in the parameter theEvent, the event record for the keyboard event. If your monitors extension includes an editable text item and the user issues a Cut, Copy, or Paste command using the Command-key equivalent, the Monitors control panel passes this event to your monitors extension function in the event record.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
6 JUL 1996