Important: The information in this document is obsolete and should not be used for new development.
Processing Events
Applications receive events one at a time by asking the Event Manager for the next available event. You use Event Manager routines to receive (or in the case ofEventAvail
, simply to look at) the next available event that is pending for your application. You supply an event record as a parameter to the Event Manager routines that retrieve events. The Event Manager fills out the event record with the relevant information about that event and returns it to your application.Your application can use the
WaitNextEvent
function to retrieve events from the Event Manager. If no events are pending for your application, theWaitNextEvent
function may allocate processing time to other applications. If an event is pending for your application, theWaitNextEvent
function returns the next available event of a specified type and removes the returned event from your application's event stream.The
EventAvail
function gets the next available event of a specified type and returns it to your application, but does not remove the event from your application's event stream.EventAvail
thus allows your application to look at an event in the event stream without actually processing the event.
- Note
- You can also use the
GetNextEvent
function to retrieve and remove an event; however, you should useWaitNextEvent
to provide greater support for multitasking.Using the WaitNextEvent Function
Your application typically callsWaitNextEvent
repeatedly. The next section, "Writing an Event Loop," shows how to useWaitNextEvent
with other routines to process events. This discussion focuses on theWaitNextEvent
function itself.The
WaitNextEvent
function requires four parameters:
When
- an event mask (
eventMask
)- an event record (
theEvent
)- a sleep value (
sleep
)- a mouse region (
mouseRgn
)
WaitNextEvent
returns, the event record contains information about the retrieved event, if any.The
eventMask
parameter specifies the events you are interested in receiving.WaitNextEvent
returns events one at a time, in order of priority and at your application's request, according to the value you specify in theeventMask
parameter. If your application specifies that it doesn't want to receive particular types of events, those events are not returned to your application when it makes a request for an event. However, those events are not removed from the event stream. (To remove events from the Operating System event queue, you can use theFlushEvents
procedure with a mask specifying only those events you wish to remove from the queue.) See "Setting the Event Mask" beginning on page 2-26 for examples of how to use constants to set the value of theeventMask
parameter.The
sleep
parameter specifies the amount of time (in ticks) for which your application agrees to relinquish the processor if no events are pending for it. When that time expires or when an event becomes available for your application, the Process Manager schedules your application for execution. In general, you should specify a value greater than 0 in thesleep
parameter so that other applications can receive processing time if they need it. If the user is editing text and your application needs to blink the caret at periodic intervals or uses TextEdit to blink the caret, your application should not specify a value greater than the value returned by theGetCaretTime
function.In the
mouseRgn
parameter you specify a screen region inside of which the Event Manager does not generate mouse-moved events. You should specify the region in
global coordinates. If the user moves the cursor outside of this region and your application is the foreground process, the Event Manager reports mouse-moved events. Your application should recalculate themouseRgn
parameter when it receives a mouse-moved event; otherwise it will continue to receive mouse-moved events as long as the cursor is outside of the original region. If you pass an empty region or aNIL
region handle, the Event Manager does not return mouse-moved events. You can use themouseRgn
parameter as a convenient way to change the shape of the cursor--for example, when the user moves the cursor from the content area of a window to the scroll bar. See "Responding to Mouse-Moved Events" beginning on page 2-62 for information on how to set and change themouseRgn
parameter.Listing 2-1 shows an example of using the
WaitNextEvent
function.Listing 2-1 Using the
WaitNextEvent
function
VAR eventMask: Integer; event: EventRecord; cursorRgn: RgnHandle; mySleep: LongInt; gotEvent: Boolean; eventMask := everyEvent; {accept all events} mySleep := MyGetSleep; {set an appropriate sleep value} cursorRgn := MyGetRgn; {set the region as appropriate} gotEvent := WaitNextEvent(eventMask,event,mySleep,cursorRgn);The code in Listing 2-1 specifies thatWaitNextEvent
should return the next pending event of any kind, give up the processor if no events are pending, and return a mouse-moved event if the user moves the cursor out of the specified region.The
WaitNextEvent
function returns after retrieving an event or after the time specified in thesleep
parameter has expired. If there are no events of the types specified by theeventMask
parameter (other than null events) pending for your application, and the time specified in thesleep
parameter has not expired,WaitNextEvent
may allocate processing time to background processes. Once an
event for your application occurs or the time specified in thesleep
parameter
expires, your application receives processing time again.
WaitNextEvent
returns a function result ofTRUE
if it has retrieved any event other than a null event. If there are no events of the types specified by theeventMask
parameter (other than null events) pending for the application,WaitNextEvent
returnsFALSE
.Before returning an event to your application,
WaitNextEvent
performs other processing and may intercept the event. TheWaitNextEvent
function:
In System 7, the
- Calls the Operating System Event Manager function
SystemEvent
to determine whether the event should be handled by your application or the Operating System. For example, if the event is a Command-Shift-number key sequence, the Event Manager intercepts the event and calls the corresponding 'FKEY
' resource to perform the associated action.- Makes the alarm go off if the alarm is set and the current time is the alarm time. The user sets the alarm using the Alarm Clock desk accessory.
- Calls the
SystemTask
procedure, which gives time to each open desk accessory or device driver to perform any periodic action defined for it. A desk accessory or device driver specifies how often the periodic action should occur, andSystemTask
gives time to the desk accessory or device driver at the appropriate interval.
WaitNextEvent
function reports a suspend event to your
application when
After your application is switched out, the Event Manager directs events (other than events your application can receive in the background) to the newly activated process until the user switches back to your application or another application.
- your application is in the foreground and the user opens a desk accessory or other item from the Apple menu,
- the user clicks in the window belonging to a desk accessory or another application, or
- the user chooses another process from the Application menu.
Writing an Event Loop
In applications that are event-driven (that is, applications that decide what to do at any time by receiving and responding to events), you can obtain information about pending events by calling Event Manager routines. Since you call these routines repeatedly, the section of code in which you request events from the Event Manager usually takes the form of a loop; this section of code is called the event loop.Listing 2-2 shows a simple event loop (an application-defined procedure called
MyEventLoop
) for an application running in System 7.
PROCEDURE MyEventLoop; VAR cursorRgn: RgnHandle; gotEvent: Boolean; event: EventRecord; BEGIN cursorRgn := NewRgn; {pass an empty region the first time thru} REPEAT gotEvent := WaitNextEvent(everyEvent, event, MyGetSleep, cursorRgn); IF (event.what <> kHighLevelEvent) AND (NOT gInBackground) THEN MyAdjustCursor(event.where, cursorRgn); IF gotEvent THEN {the event isn't a null event, } DoEvent(event) { so handle it} ELSE {no event (other than null) to handle } DoIdle(event); { right now, so do idle processing} UNTIL gDone; {loop until user quits} END;TheMyEventLoop
procedure repeatedly usesWaitNextEvent
to retrieve events. TheWaitNextEvent
function returns a Boolean value ofFALSE
if there are no events of the specified types other than null events pending for the application.WaitNextEvent
returnsTRUE
if it has retrieved any event other than a null event.After
WaitNextEvent
returns, theMyEventLoop
procedure first calls an application- defined routine,MyAdjustCursor
, to adjust the cursor as necessary. You usually adjust the cursor in response to mouse-moved events, and often in response to other events as well. This code adjusts the cursor once every time through the event loop, when the application receives any event other than a high-level event. The code does not adjust the cursor if the event is a high-level event, because thewhere
field of a high-level event contains the event ID, not the location of the cursor. The code also does not adjust the cursor if this application is in the background, as the foreground process is responsible for setting the appearance of the cursor.If
WaitNextEvent
retrieved any event other than a null event, the event loop callsDoEvent
, an application-defined procedure, to process the event. Otherwise, the procedure calls an application-defined idling procedure,DoIdle
.
If you intend to design your application to run in either a single-application environment (such as System 6 without MultiFinder) or a multiple-application environment,
- Note
- If your application uses modeless dialog boxes, you need to appropriately handle events in them. You can choose to handle events for modeless dialog boxes using the same routines that you use to handle events in other windows; this is the approach used throughout this chapter. Alternatively, you can choose to call the
IsDialogEvent
function in your event loop. See "Handling Events in a Dialog Box" on page 2-29 for information on handling events in alert boxes, modal dialog boxes, movable modal dialog boxes, and modeless dialog boxes. For additional information on dialog boxes, see the chapter "Dialog Manager" in this book.
the very beginning of your event loop should test to make sure theWaitNextEvent
function is available. IfWaitNextEvent
is not available, your code should useGetNextEvent
to retrieve events. If your code usesGetNextEvent
, it should also
callSystemTask
to allow desk accessories to perform periodic actions. However,
your code should always useWaitNextEvent
if it is available, rather thanGetNextEvent
. If your application callsWaitNextEvent
, it should not call theSystemTask
procedure.The event loop shown in Listing 2-2 calls an application-defined procedure,
DoEvent, to
determine what kind of event the call toWaitNextEvent
retrieved. Listing 2-3 defines a simpleDoEvent
procedure. TheDoEvent
procedure examines the value of thewhat
field of the event record to determine the type of event received and then calls an appropriate application-defined routine to further process the event.
PROCEDURE DoEvent (event: EventRecord); VAR window: WindowPtr; activate: Boolean; BEGIN CASE event.what OF mouseDown: DoMouseDown(event); mouseUp: DoMouseUp(event); keyDown, autoKey: DoKeyDown(event); activateEvt: BEGIN window := WindowPtr(event.message); activate := BAnd(event.modifiers, activeFlag) <> 0; DoActivate(window, activate, event); END; updateEvt: DoUpdate(WindowPtr(event.message)); diskEvt: DoDiskEvent(event); osEvt: DoOSEvent(event); kHighLevelEvent: DoHighLevelEvent(event); END; {of case} END;The next sections describe how to set the event mask, handle events in dialog boxes,
and create your application's'SIZE'
resource. Following sections show code that can handle each kind of event.Setting the Event Mask
Several of the Event Manager routines can be restricted to operate on a specific event type or group of types. You do this by specifying the event types you want your application to receive, thereby disabling (or "masking out") the events you are not interested in receiving. To specify which event types an Event Manager routine governs, you supply a parameter known as an event mask.The event mask is an integer with one bit position for each event type. If the bit representing a particular event type is set, then the Event Manager returns events of
that type. If the bit is set to 0, the Event Manager does not return events of that type. To accept all types of events, set every bit of the event mask to 1. You can do this using the constanteveryEvent
.
CONST everyEvent = -1; {every event}Figure 2-6 shows the bits corresponding to each event type in the event mask.
You can use these constants when referring to the bits in the event mask that correspond to each individual event type:
CONST mDownMask = 2; {mouse-down event (bit 1)} mUpMask = 4; {mouse-up event (bit 2)} keyDownMask = 8; {key-down event (bit 3)} keyUpMask = 16; {key-up event (bit 4)} autoKeyMask = 32; {auto-key event (bit 5)} updateMask = 64; {update event (bit 6)} diskMask = 128; {disk-inserted event (bit 7)} activMask = 256; {activate event (bit 8)} highLevelEventMask = 1024; {high-level event (bit 10)} osMask = -32768; {operating-system event (bit 15)}You can select any subset of events by adding or subtracting these constants. For example, you can use this code to accept only high-level events and mouse-down events and mask out all other events:
myErr := WaitNextEvent(highLevelEventMask + mDownMask, myEvent, mySleep, myMRgnHnd);TheeveryEvent
constant indicates that you wish to receive every type of event. To accept all events except mouse-up events, you can use the code:
myErr := WaitNextEvent(everyEvent - mUpMask, myEvent, mySleep, myMRgnHnd);Masking out specific types of events does not remove those events from the event stream. If a type of event is masked out, the Event Manager simply ignores it when reporting events from the event stream. Note that you cannot mask out null events by setting the event mask. The Event Manager always returns a null event if no other events are pending. However, if you do not want the Event Manager to report null events to your application when it is in the background, you can set thecannotBackground
flag in your application's'SIZE'
resource.In most cases you should always use
everyEvent
as your event mask. The user expects most applications to respond to keyboard, mouse, update, and other events.The types of events returned to your application are also affected by the system event mask. The Event Manager maintains a system event mask for each application. The system event mask controls which low-level event types get posted in the Operating System event queue. The Event Manager uses the system event mask of the current process (the process that is currently executing and the process associated with the
CurrentA5
global variable) when determining which low-level events to post in the Operating System event queue. The system event mask is an integer with 1 bit for
each corresponding low-level event type. These constants refer to the bits that represent the corresponding low-level event types in the system event mask:
CONST mDownMask = 2; {mouse-down (bit 1)} mUpMask = 4; {mouse-up (bit 2)} keyDownMask = 8; {key-down (bit 3)} keyUpMask = 16; {key-up (bit 4)} autoKeyMask = 32; {auto-key (bit 5)} diskMask = 128; {disk-inserted (bit 7)}When a low-level event (other than an update or activate event) occurs, the Operating System Event Manager posts the low-level event in the Operating System event queue only if the bit corresponding to the low-level event type is set in the system event mask of the current process. When your application starts, the Operating System initializes the system event mask of your application to post mouse-up, mouse-down, key-down, auto-key, and disk-inserted events in the Operating System event queue. Thus, the system event mask has this initial setting:
systemEventMask := everyEvent - keyUpMask;Your application should not change the system event mask except to enable key-up events if your application needs to respond to key-up events. (Most applications ignore key-up events.) If your application needs to receive key-up events, you can change the system event mask using the Operating System Event Manager procedureSetEventMask. Note that your application cannot rely on receiving key-up events when it is not the current process. For example, if your application is the foreground (and current) process and a minor switch occurs, the Event Manager uses the system event mask of the background process (now the current process) when posting low-level event types. When your application becomes the current process again, the Event Manager uses the system event mask of your application when posting low-level events.
Handling Events in a Dialog Box
If your application uses alert boxes, modal dialog boxes, movable modal dialog boxes,
or modeless dialog boxes, you need to make sure your application handles events for them appropriately.To display and handle events in alert boxes, you use the Dialog Manager functions
Alert
,NoteAlert
,CautionAlert
, andStopAlert
. The Dialog Manager handles all of the events generated by the user until the user clicks a button (typically the OK or Cancel button). When the user clicks the OK or Cancel button, the alert box functions highlight the button that was clicked, close the alert box, and report the user's selection to your application. Your application is responsible for performing the appropriate action associated with that button.For modal dialog boxes, you can use the Dialog Manager procedure
ModalDialog
. The Dialog Manager handles most of the user interaction until the user selects an item. TheModalDialog
procedure then reports that the user selected an enabled item, and your application is responsible for performing the action associated with that item. Your application typically callsModalDialog
repeatedly, responding to clicks on enabled items as reported byModalDialog
, until the user selects OK or Cancel.For alert boxes and modal dialog boxes, you should also supply an event filter function as one of the parameters to the alert box functions or
ModalDialog
procedure. As the user interacts with the alert or modal dialog box, these functions pass events to your event filter function before handling each event. Your event filter function can handle any events not handled by the Dialog Manager or, if necessary, can choose to handle events normally handled by the Dialog Manager. For more information on filter functions for alert and dialog boxes, see the chapter "Dialog Manager" in this book.To handle events in movable modal dialog boxes, you can use the Dialog Manager functions
IsDialogEvent
andDialogSelect
or you can use other Toolbox routines to handle events without using the Dialog Manager.For modeless dialog boxes, you can choose to handle events in them using an approach similar to the one you use to handle events in other windows; that is, when you receive an event, you first determine the type of event that occurred and then take the appropriate action based on the type of window that is in front. If a modeless dialog box is in front, you can provide code that takes any actions specific to that modeless dialog box and call the
DialogSelect
function to handle any events that your code doesn't handle. This is the approach used throughout this chapter. Alternatively, you can choose to call theIsDialogEvent
function in your event loop. If you do this, you can use theIsDialogEvent
function to determine whether the event involves a modeless dialog box that belongs to your application. If the event involves a modeless dialog box (including null events) and a modeless dialog box is active,IsDialogEvent
returnsTRUE
. Otherwise,IsDialogEvent
returnsFALSE
.If
IsDialogEvent
returnsTRUE
, your application can check to see what type of event occurred and, depending on the type of event, it can choose to handle the event itself.Regardless of the approach you use, if your application chooses not to handle the event, it should call
DialogSelect
. TheDialogSelect
function handles events for modeless dialog boxes (including null events). It also blinks the caret in editable text items when necessary.For more information on the
DialogSelect
function and events in dialog boxes, see the chapter "Dialog Manager" in this book.