Important: The information in this document is obsolete and should not be used for new development.
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. Themessage
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;Themessage
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 themessage
parameter indicates the action your monitors extension function should perform:
In addition, the
startupMsg
. Informs your monitors extension function that it has been loaded into memory. Your function can determine whether the user has superuser status by examining theitem
parameter. The Monitors control panel sets theitem
parameter to 1 if the user is a superuser. Your code should load any resources and modify them if necessary for the capabilities of the computer system or selection of superuser status. You can also allocate memory in response to this message, and store the value identifying the user's status.initMsg
. Requests your monitors extension function to perform initialization.okMsg
. Indicates that the user clicked the OK button. Your function should check for any values the user changed, release any memory it allocated, and return control to the Monitors control panel.cancelMsg
. Indicates that the user clicked the Cancel button. Your function should restore the system to the state it was in before the user clicked the Options button, release any memory it allocated, and return control to the Monitors control panel. If the user modified any values before clicking the Cancel button, reinstate the original values.hitMsg
. Indicates that the user clicked an enabled control in your monitors extension. Your function should handle the click.nulMsg
. Requests your control device function to handle a null event by performing any idle processing. Your monitors extension function should do minimal processing in response to a null event; for example, it should not refresh control settings. The Monitors control panel passes the event record for the null event in the parametertheEvent
.keyEvtMsg
. Requests your monitors extension function to handle a key-down or auto-key event.updateMsg
. Requests your monitors extension function to update any user items and redraw any controls that are not standard dialog items handled by the Dialog Manager.
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:
activateMsg
. Requests your monitors extension function to respond to an activate event by making your video card's controls active. Currently, this message is not used because the Options dialog box is modal. However, your monitors extension function should handle this message as it would any activate event because in future implementations the Options dialog box might be modeless.deactivateMsg
. Requests your monitors extension function to respond to an activate event by making your video card's controls inactive. Currently, this message is not used because the Options dialog box is modal. However, your monitors extension function should handle this message as it would any activate event because in future implementations the Options dialog box might be modeless.superMsg
. Informs your monitors extension function that the user has selected superuser status. This message is provided for backward compatibility with System 6. However, your monitors extension function can respond to it by initializing any controls that you have reserved for superusers, if your function has not already done so in response to either thestartupMsg
orinitMsg
message. If your function does not handle this message, it should return as its function result a handle to any memory it previously allocated. The Monitors control panel sends the messagesuperMsg
ornormalMsg
immediately following the initialization message.normalMsg
. Informs your monitors extension function that the user has not selected superuser status. This message is provided for backward compatibility with System 6. However, your monitors extension function can respond to it by initializing any controls, if your code has not already done so in response to either thestartupMsg
orinitMsg
message. If your function does not handle this message, it should return as its function result a handle to any memory it previously allocated. The Monitors control panel sends the messagenormalMsg
orsuperMsg
immediately following the initialization message.
For a description of the remaining parameters of the monitors extension function, see "Monitors Extension Functions" beginning on page 8-79.
- 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.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 aCASE
statement that handles messages that the Monitors control panel passes toMyMonExtend
. 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 themonitorValue
parameter, the previous function result. TheMyMonExtend
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 theitem
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, theitem
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 theMyMonExtend
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;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.
- 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.After it allocates storage, the function shown in Listing 8-26 calls its own
MySetUpData
function to check the value of theitem
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 aninitMsg
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 theMyMonExtend
function calls to handle the initialization message. FirstMyHandleInitMsg
sets its controls to their initial values;MyHandleInitMsg
calls the Dialog Manager'sGetDialogItem
and the Control Manager'sSetControlValue
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 theMyDrawRect
procedure, which draws the line dividing superuser controls from other controls. TheMyDrawRect
procedure uses theFrameRect
procedure to draw a 1-pixel-high rectangle. Note thatMyDrawRect
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 theFrameRect
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 ownMySaveNewValues
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 ahitMsg
message. Your monitors extension function typically changes the setting of the control or performs the appropriate action in response to ahitMsg
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
- In the
item
parameter, the number of the item clicked. This is not the number you assign in your item list, but the number after the Monitors control panel appends your item list to the item list of the Options dialog box.- In the
numItems
parameter, the number of items in the item list of the standard Options dialog box.- In the parameter
theEvent
, the event record for the mouse event that generated thehitMsg
message.
numItems
fromitem
.Listing 8-30 shows the
MyHandleHits
procedure, whichMyMonExtend
calls to handle ahitMsg
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. ThenMyHandleHits
calls the Dialog Manager'sGetDialogItem
procedure and the Control Manager'sSetControlValue
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 thekeyEvtMsg
message. The Monitors control panel passes, in the parametertheEvent
, 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.