Events and Event Handlers in OS X

The number of different types of events that can occur in a Mac environment is vast. A mouse button click on a control, a mouse button click on a menu, a window collapsing, expanding, zooming, or closing, a disc inserted into the computer, and many, many other actions cause the generation of an event. Your program won't need to watch for, or respond to, every type of event. Instead, with expert author Dan Parks Sydow, you'll define the event types for which your program should watch.

This article is excerpted from Mac OS X Programming, by Dan Parks Sydow.

From the author of

From the author of

The number of different types of events that can occur is vast, but your program won't need to watch for, or respond to, every type of
event. Instead, you'll define the event types for which your program should
watch. You'll relay this information, along with information about an
application-defined routine that handles the event, to the Carbon Event Manager.
The routine is one that you write for the purpose of handling a particular
event. It specifies what your program should do in response to the occurrence of
an event of interest. Such a routine is called an event handler. After
these steps are completed, you don't have to write any additional
event-handling code because now the responsibility of watching for and handling
particular events falls on the Carbon Event Manager.

Event Types

To distinguish one event from another, each Carbon event has an event class
and an event kind associated with it. The event class specifies the broad
category, such as a mouse event, to which the event belongs. The event
kind further hones in on the particular nature of the event by specifying
the kind of event, such as a mouse-down event, within the class. Together, the
event class and event kind are referred to as an event type.

A commonly occurring event is the mouse-down event. Such an event is
generated in response to a user clicking the mouse button. A click of the mouse
button results in an event that has an event class of mouse event and an event
kind of mouse-down. That wording might be understandable to you, but of course,
the operating system needs that information in a more "code-like"
manner. Apple defines a wealth of event class and event kind constants for this
purpose. For a mouse click, the pertinent constants are as follows:

KEventClassMouse = FOUR_CHAR_CODE('mous');
KEventMouseDown = 1;

Both the event class constant and the event kind constant are integers,
although the event class constant is specified by a four-character value that
gets translated to a 32-bit integer (by way of the FOUR_CHAR_CODE
macro).

In all cases, you'll be pleased to know that you aren't responsible
for knowing any of the actual four-character or integer values. You need only
become familiar with some of the constants with which you'll be working.
The CarbonEvents.h header file lists them all. Tables 3.1 and 3.2 introduce you
to two important event classes: the mouse event class
(kEventClassMouse) and the window event class
(kEventClassWindow). These tables list a few (but not all) of the event
kinds in those two classes.

3.1 The Mouse Event Class (kEventClassMouse) and Some of Its Event
Kinds

Event Kind

Cause of Event Kind

kEventMouseDown Mouse

Mouse button clicked

kEventMouseUp

Mouse button released

kEventMouseMoved

Mouse position changed

kEventMouseDragged

Mouse dragged

3.2 The Window Event Class (kEventClassWindow) and
Some of Its Event Kinds

Event Kind

Cause of Event Kind

kEventWindowActivated

Window brought to front

kEventWindowDeactivated

Window sent behind

kEventWindowDrawContent

Draw (update) window's contents

kEventWindowShown

Window became visible

kEventWindowHidden

Window became invisible

kEventWindowCollapsed

Window collapsed (minimized to Dock)

kEventWindowExpanded

Window expanded (enlarged from Dock)

kEventWindowZoomed

Window zoomed

When an event class and an event type are paired, the result
specifies one and only one type of event. Such a pairing is called an event
type specifier. The EventTypeSpec is a structure that represents an
event type specifier:

struct EventTypeSpec
{
UInt32 eventClass;
UInt32 eventKind;
};

Each event type has an event class and an event kind. Some event types also
have event parameters (also called event attributes). The number, and
purpose, of an event's parameters depends on the event type in question.
For instance, the already-discussed mouse-down event type, which has an event
class of kEventClassMouse and an event kind of
kEventMouseDown, has four parameters that hold further information
about the mouse-down event. Table 3.3 lists these event parameters and their
purposes.

3.3 The Mouse-Down Event Parameters

Parameter

Represents

kEventParamMouseLocation

Location of cursor at time of mouse click

kEventParamKeyModifiers

Modifier keys pressed at time of mouse click

kEventParamMouseButton

Mouse button clicked (for the instance of a multiple-button mouse)

kEventParamClickCount

Number of mouse clicks (such as a double-click)

The EventTypeSpec keeps track of just two pieces of
informationthe event class and event kindto define an event type. A
different data type, the EventRef, keeps track of this same information
and event parameter information. As shown in the struct
definition, the composition of an EventTypeSpec structure is
documented. The same isn't true of the EventRef data type.

The EventRef is an opaque type. Apple would prefer that a programmer
not know its exact makeup. Making a data type opaque assures Apple that they can
alter the internals of the data type at a future date without being concerned
that programmers have written code that directly accesses fields of the data
type.

When working with events, you'll declare a variable of type
EventTypeSpec and then fill in the two fields of that structure.
You'll also make use of an EventRef variable, but you'll
often enable the system to fill in that structure's fields.

Your first step in handling an event is defining the event type in which your
program is interested. To do this, declare an event type specifier for the event
type of interest. For the upcoming discussion of installing an event handler,
consider a program that's waiting for a click a button in a window. The
class of such an event is considered a command, and the event kind is the
processing of that command. For this situation, the event type specifier might
look like this:

Installing an Event Handler

One of the primary jobs of the Carbon Event Manager system software is to
watch for events. When this system software encounters an event about which your
program is to be notified, it passes that event on to your program. In
particular, it sends the event to a routine that you've written
specifically to handle this type of event. Such a routine is an event handler.
You'll need to write this routine (that task is covered next), and
you'll need to install this routine.

To install the event handler means to provide the Carbon Event Manager
system software with an association between the event type specifier (the type
of event to be handled) and the event handler (the application-defined routine
that should be executed at the occurrence of an event of the specified type).
Pairing an event type with an event handler routine and making the Carbon Event
Manager aware of this pairing requires a call to the Carbon API routine
InstallEventHandler. Here's the prototype for that function:

A program specifies an event type to watch for, and it defines an event
handler routine to handle an occurrence of such an event. When the Carbon Event
Manager encounters an event of the specified type and invokes the appropriate
event handler routine, it needs to know upon what object the handler should act.
In other words, you need to specify the target of the event. A target is
a window, menu, control, or even the application itself. The target you specify
should be the object "closest" to the event. For instance, if a
program is watching for events that occur in a window, the window can be
considered the target. Simply telling the Carbon Event Manager that a window is
to be the target is not enough, though. You need to tell the Carbon Event
Manager which window is the target. In addition, you need to supply this
information as an EventTargetRef variable rather than as the window
reference itself.

This target information is sent to the Carbon Event Manager by way of the
first InstallEventHandler parameter, which is the target. You can
create an EventTargetRef value by passing the intended target to the
proper target routines: GetWindowEventTarget,
GetMenuEventTarget, GetControlEventTarget, or
GetApplicationEventTarget. Each routine takes the one argument passed
to it and generates an EventTargetRef from that argument. In the
following code snippet, the target of an event handler routine is to be a window
that's referenced by the WindowRef variable window, and it's
assumed that this window has already been created by a call to
CreateWindowFromNib. The following code would generate an
EventTargetRef that then could be used as the first argument to
InstallEventHandler:

The second InstallEventHandler parameter is handlerProc, an
EventHandlerUPP. The UPP in EventHandlerUPP stands for
universal procedure pointer. In short, any time you see UPP in a
variable or type name, you can expect that a pointer to a routine is involved.
If the Carbon Event Manager is to invoke the event handler function, it needs to
know where that function's code is in memory. The handlerProc
variable holds a pointer to the event handler routine. You can generate such a
pointer by calling the NewEventHandlerUPP function. Simply pass the
name of the event handler routine to NewEventHandlerUPP and the
function returns a pointer to that function. Assuming that the as-yet unwritten
event handler routine will be called MyEventHandler, the call to
NewEventHandlerUPP appears as shown in the following code. The
resulting EventHandlerUPP then could be passed as the second argument
to InstallEventHandler.

The third InstallEventHandler parameter, which is numTypes,
is the number of event types to which this one event handler can respond.
Although so far the focus has been on one event type and one event handler
routine, it is possible to have a single event handler that's capable of
handling more than one type of event.

The next parameter is typeLista pointer to the event type or
event types that this event handler routine handles. As mentioned, an event
handler can serve more than one event type. Here's where the Carbon Event
Manager gets the EventTypeSpec for each event type. The
typeList variable is a pointer that points to either one
EventTypeSpec or to an array of EventTypeSpecs.

The userData parameter is used to pass a pointer to any information
that might be of use to the event handler. The pointer is passed to the Carbon
Event Manager, which in turn passes the pointer to the event handler routine
each time it's called. One common use for this pointer is to use it to pass
a pointer to the window in which the event took place. For the handling of some
event types, it might not make sense to pass supplemental information, and in
such a case, you can use a value of NULL here.

The last InstallEventHandler parameter is a pointer to an event
handler reference. This is a value that the Carbon Event Manager fills in for
use by your program. Your program will need to use this value only if your
program will be dynamically changing the event types that make use of the event
handler routine. This is a situation you won't encounter often, so expect
to simply pass a value of NULL here.

Now let's gather things together and take a look at a snippet that
defines one type of event and installs an event handler that's to handle
events of that type. The event type is used to watch for a command (such as a
mouse click on a button in a window), and the event handler routine that will
handle such an event is named MyEventHandler.