Chapter 6. Carbon Events

Everything a typical Carbon application does, whether interacting with the user or communicating with the system, takes place in response to an event sent to the application by the operating system. Events include any activity that requires a response by the application—user actions, changes in processing status, hardware activity, and other occurrences. The core task of any Carbon application is to respond to events.

Carbon supports two event-handling models, although only one is recommended. The first is the legacy Mac OS Event Manager model, referred to as the classic Event Manager. The second is the Carbon Event Manager model. It’s the one we’ll cover in this chapter, and the event model your Carbon application should use. An application that uses this model tells the Carbon Event Manager what events are of interest. Then the application doesn’t do anything until the Carbon Event Manager detects one of the events the application wants to handle. So, when someone using your application is asleep at the keyboard, your application is simply waiting. No processing cycles are wasted; it’s a very efficient model.

Another big advantage to using the Carbon Event Manager is that it greatly reduces the amount of code needed to write a basic application. It provides standard handlers for most types of user interaction, so you can concentrate on writing code that’s unique to your application.

In this chapter, you’ll look at Carbon events and how to handle them. In particular, you’ll:

  • Get an overview of how events are handled with the Carbon Event Manager

  • Learn the essential terminology—event targets, types, and references

  • See what default handlers can do for you

  • Write a Carbon event handler

Carbon Event Handling

Let’s follow a typical user event—a mouse click-through the system. The user clicks a button in the interface. The action of the user pressing a mouse button sets off a low-level event from the device driver that controls the mouse. The I/O Kit, which forms the foundation of all device drivers on Mac OS X, creates the event, puts it in the window server’s event queue, and notifies the window server. (The window server is a core service that manages windows. All user actions occur in a window or in a menu associated with a window.) The window server takes the event off the queue, consults a database of open windows, then sends the event to the process that owns the window in which the event occurred. The Carbon Event Manager gets the event from the window server, packages it in an appropriate form, and passes it to the event-handler mechanism specific to the application. This mechanism ensures that the event is handled by the function associated with the control the user clicked. Figure 6.1 depicts the subsystems that generate, repackage, and forward an event to its destination.

A mouse click handled by Mac OS X and the Carbon Event Manager

Figure 6-1. A mouse click handled by Mac OS X and the Carbon Event Manager

Carbon uses a callback mechanism to handle events. You define your application’s response to various types of events by writing event handler functions and registering them with the Carbon Event Manager. Then, each time an event occurs, the Carbon Event Manager calls back the handler function you installed for that type of event.

Implementing handler-based event management in your application is fairly straightforward. It involves these steps:

  1. Identify the events your application must handle.

  2. Write handlers (functions) to respond to the events.

  3. Install the handlers. This informs the Carbon Event Manager of the events your application handles and which handlers respond to those events.

  4. Call the function RunApplicationEventLoop. The Carbon Event Manager does the rest. When the Carbon Event Manager detects an event that your application handles, the manager calls your handler.

The tricky part is to figure out, in Carbon Event Manager lingo, to what events your application responds. This involves learning some new terms and then looking up some predefined constants in the Carbon Event Manager documentation. Basically, you must:

  • Identify where events of interest to your application occur (the event target)

  • Specify what events are of interest (event type) to your application

Once you’ve defined what you’re looking for, your application must be prepared to dig out event information from an event reference it gets from the Carbon Event Manager.

Event Targets

Carbon events happen, at a general level, in a window or in a menu. On a more specific level, events occur in a user pane, a control, a radio button, and so forth. The interface object (window, control, etc.) in which an event occurs is called the event target. When you install an event handler you must specify an event target.

An event target can have any number of events associated with it. For example, an event handler for an application’s main window (the event target) could process command events, window events, and mouse events.

Event Types

An event type is a pair of values—an event class and an event kind—that define an event. As you identify the events your application handles, you need to find the pair of constants (event class and event kind) associated with each event.

Most Carbon events fall into one of nine event classes: application, command, control, keyboard, menu, mouse, tablet, text input, and window.

  • Application. Associated with application-wide events, such as quitting and launching.

  • Command. Issued from such objects as menu items and buttons in the interface.

  • Control. Associated with controls in the interface, such as radio buttons, checkboxes, and sliders.

  • Keyboard, mouse, and tablet. Issued by the device of the same name.

  • Menu and window. Actions that happen in menus and windows, such as opening a menu or closing a window.

  • Text input. Specialized text events, such as mapping a character index to a screen position.

Table 6.1 lists the major event classes and the Carbon Event Manager constants you need to use when you refer to an event class in your application.

Table 6-1. Carbon Event Classes

Event ClassEvent Class Constant
Application kEventClassApplication
Command kEventClassCommand
Control kEventClassControl
Keyboard kEventClassKeyboard
Menu kEventClassMenu
Mouse kEventClassMouse
Tablet kEventClassTablet
Text Input kEventClassTextInput
Window kEventClassWindow

An event kind indicates a specific event within an event class. Some event classes have only a few event kinds (such as the command class); others (such as the window class) have many event kinds. To define an event type, in addition to the event class, you’ll need to look up the constant associated with the event kind. You can find the event kind constants for each of the event classes listed in Appendix B.

When you specify an event in your application, you use a structure of type EventTypeSpec. Let’s say your application is supposed to do something every time the user clicks a button in the interface. The event you want to handle is classified as a command (a command class event). The specific action you want to take is to process the command. You’d specify the event type as follows:

EventTypeSpec myEventSpecification = {kEventClassCommand,
    kEventCommandProcess};

Event References

When the Carbon Event Manager detects an event for your application, it returns an event reference to your application. The event reference is a pointer to an opaque structure that contains general information about the event’s class, kind, and time of occurrence. An opaque structure is a data structure you can’t “open up” to directly examine or manipulate its internal elements. You can, however, obtain information about the event’s attributes by passing the event reference to various Carbon Event Manager accessor functions provided for this purpose. For instance, the functions Get EventClass and GetEventKind each accept an event reference as a parameter and return a 32-bit integer representing the event’s class and kind, respectively:

EventRef theEvent;
UInt32    eventClass;
UInt32    eventKind;
eventClass = GetEventClass (theEvent);
eventKind  = GetEventKind (theEvent);

Similarly, the Carbon Event Manager function GetEventTime returns the time an event occurred, expressed as a double-length integer of type EventTime measured in seconds since the system was started up:

EventRef   theEvent;
EventTime  timeInSeconds;

timeInSeconds = GetEventTime (theEvent);

In addition to generic event attributes like class, kind, and time, an event can have additional event parameters whose number and nature vary depending on the event type. Each parameter has an event parameter name and an event parameter type, both of which are denoted by constants defined in the Carbon interface. For instance, a mouse-down event has these four event parameters:

  • kEventParamMouseLocation. A point (parameter type typeQDPoint) giving the global screen coordinates at which the mouse button was pressed.

  • kEventParamMouseButton. An integer code (parameter type typeMouseButton) identifying which button was pressed (allowing support for a one-, two-, or three-button mouse).

  • kEventParamKeyModifiers. A set of flag bits (parameter type typeUInt32) telling which modifier keys, if any, were being held down at the time the button was pressed.

  • kEventParamClickCount. An integer (parameter type typeUInt32) telling how many times the button was clicked in the same location (1 for a single click, 2 for a double click, and so on).

Other event types have different parameters associated with them; see Appendix C. All such parameters are accessed with the same Carbon Event Manager function, GetEventParameter, which takes as arguments an event reference, the name and type of the requested parameter, the size of the expected value in bytes, and a pointer to a memory buffer of that size in which to return the value. (There is also a pair of arguments for returning the actual parameter type and size of the value returned; you can specify NULL for these arguments if you don’t need this information or don’t expect the actual type and size to differ from those requested.) Thus, for example, you might obtain the screen coordinates for a mouse-down event as follows:

EventRef  theEvent;
Point     mousePoint;

GetEventParameter (theEvent,
                kEventParamMouseLocation,
                typeQDPoint, NULL,
                sizeof(mousePoint), NULL,
                &mousePoint);

Default Event Handlers

Carbon provides a default event handler for each type of event target (window, menu, control, and application). The default handler defines a standard response to each type of event that a particular target may receive. The one for windows, for instance, implements all the standard behavior for manipulating a window with the mouse—dragging it by its title bar, closing it by clicking the Close button, resizing it by dragging the resize control, and so on. By installing the default handler when you create a window, you automatically inherit all of this standard behavior with no additional effort on your part. You can then proceed to install additional handlers of your own for those aspects of the window’s behavior that are specific to your individual application, such as drawing the window contents or responding to the user’s mouse actions inside it. Events of those specific types will be reported to your own installed handlers for processing; all others will instead be passed through to the default handler to deal with in the standard way. This frees you from having to provide your own handler for each of the hundred or so events that Carbon may throw at you. With the default event handler to back you up, you can focus your attention on those events whose behavior you need to modify or customize in some way and leave the rest to the default handler.

Sometimes the default event handler’s response to a single event can trigger an elaborate cascade of other events. Consider, for example, what happens when the user presses the mouse button in a window’s resize control. The mouse press generates an event of type kEventMouseDown, reporting such information as the time and location at which the button was pressed, what modifier keys were being held down at the time, and so forth. Responding to this event involves hit-testing the mouse location to determine that it lies in the window’s resize control, tracking the mouse’s movements for as long as the button is held down, providing appropriate visual feedback on the screen, and finally resizing the window when the button is released. Theoretically, you could provide a handler function for mouse-down events to do all this yourself, but it’s generally more convenient to let the default event handler manage all these chores for you in the standard way. It does this by generating a sequence of further events representing various stages in the process of responding to the original mouse press:

  1. A hit-test event (kEventWindowHitTest) to analyze the mouse location and determine what object on the screen received the mouse press.

  2. A click-resize-region event (kEventWindowClickResizeRgn) indicating that the mouse button was pressed in the resize control of one of your windows.

  3. A get-minimum-size (kEventWindowGetMinimumSize) and a get-maximum-size (kEventWindowGetMaximumSize) event requesting the smallest and largest dimensions to which the user should be allowed to resize the window.

  4. The following cycle of events, repeated for as long as the mouse button is held down:

    • A mouse-dragged event (kEventMouseDragged) reporting the mouse’s coordinates.

    • A window bounds-changing event (kEventWindowBoundsChanging) indicating that the window’s size is about to change.

    • A get-grow-image-region event (kEventWindowGetGrowImageRegion) requesting the size and shape of the window outline to be drawn for visual feedback on the screen.

  5. A mouse-up event (kEventMouseUp) when the mouse button is released.

  6. A draw-frame event (kEventWindowDrawFrame) to redraw the window’s structural elements (frame, title bar, and so forth) in the new size.

  7. A window bounds-changed event (kEventWindowBoundsChanged) indicating that the window’s size has changed.

  8. A window update event (kEventWindowUpdate) indicating that the portion of the window’s contents visible on the screen has changed and must be redrawn.

  9. A draw-content event (kEventDrawContent) to redraw the window’s interior contents.

This proliferation of events may seem daunting, but most of them are really intended to be processed by the default event handler itself, with no active intervention on your part. The only reason for sending all these events is to give you the flexibility to step in at various points in the process and take control yourself if you choose to do so. Maybe you want to reimplement the draw-frame event to change the standard rectangular window frame to an octagonal viewing port for your starship simulation, or intercept mouse-dragged events to play a cool sound effect while the user is dragging the mouse around. Most of the time, you’ll just leave these events for the default handler to manage in its own way.

Defining an Event Handler

When you define your own event handler, it must conform to this prototype:

pascal OSStatus HandlerName (EventHandlerCallRef  nextHandler,
                                 EventRef             theEvent
                                 void*                userData);

where HandlerName is the name you assigned to the function.

The parameter theEvent is an event reference describing the event to be handled. Your handler can use this value to obtain information about the event by passing it to one of the Carbon accessor functions, such as GetEventClass, GetEventKind, and GetEventParameter, as discussed above.

The handler returns a status code of type OSStatus as its function result: noErr to show that it has successfully handled the specified event or eventNotHandledErr to indicate that it hasn’t. In the latter case, the Carbon Event Manager will relay the event to the next handler after this one in the chain of handlers for the given type of event, continuing up the chain until it finds a handler willing to accept and process the event. You can also explicitly propagate an event up the handler chain yourself by calling the Carbon Event Manager function CallNextEventHandler, passing it the value you received for the nextHandler parameter. This value is an event handler call reference, an opaque structure denoting the next handler after this one in the chain. This technique of event propagation is useful for incorporating the standard response to an event into your own handler while adding any desired pre- or postprocessing of your own.

The event handler’s third parameter, userData, is a pointer to an arbitrary data value that your program can use for any purpose of its own. You specify the value of this item when you install your event handler; the Carbon Event Manager will then pass this same value back to the handler function each time it’s called.

Installing an Event Handler

The basic Carbon function for installing an event handler is called InstallEventHandler:

OSStatus InstallEventHandler (EventTargetRef        target,
                             EventHandlerUPP        handlerProc,
                             UInt32                 numTypes,
                             const EventTypeSpec*   typeList,
                             void*                  userData,
                             EventHandlerRef*       handlerRef);

The second parameter, handlerProc, is a universal procedure pointer (UPP) to your handler function. The Carbon Event Manager function NewEventHandlerUPP returns a UPP of the required type; for instance:

EventHandlerUPP  handlerUPP;

handlerUPP = NewEventHandlerUPP(MyHandler);

where MyHandler is the name of your handler function.

Note

A universal procedure pointer (UPP) is a generalized procedure pointer that lets code with different calling conventions call each other. Some Carbon functions require you to pass UPPs for callbacks because the calling routine doesn’t know in advance if your code is Mach-O based or CFM-based.

The target parameter to InstallEventHandler identifies the event target on which the handler is to be installed. You can obtain a reference to the desired target by calling one of the Carbon functions GetApplicationEventTarget, GetWindowEventTarget, GetMenuEventTarget, or GetControlEventTarget. For convenience, the Carbon Event Manager also defines a set of specialized macros, InstallWindowEventHandler, InstallMenuEventHandler, and InstallControlEventHandler, which accept the targeted object as a parameter, obtain the corresponding target reference for you, and pass it to InstallEventHandler. The remaining parameters to these macros are the same as for the function InstallEventHandler. For example, the macro call:

InstallWindowEventHandler (theWindow, handlerUPP,
                              numTypes, typeList,
                              userData, &handlerRef);

is equivalent to:

theTarget = GetWindowEventTarget(theWindow);
InstallEventHandler (theTarget, handlerUPP,
                        numTypes, typeList,
                        userData, &handlerRef);

A similar macro, InstallApplicationEventHandler, needs no parameter to identify the application itself as the target; the call:

InstallApplicationEventHandler (handlerUPP,
                                 numTypes, typeList,
                                 userData, &handlerRef);

is equivalent to:

theTarget = GetApplicationEventTarget();
InstallEventHandler (theTarget, handlerUPP,
                                 numTypes, typeList,
                                 userData, &handlerRef);

In all of these cases, the typeList parameter specifies the event types for which the handler is to be installed. This parameter is nominally declared as a pointer to an event type specifier giving the class and kind of a single event type; but since the C language considers pointers and arrays to be equivalent, it may actually designate an array of such specifiers for more than one type. The numTypes parameter tells how many event types are being specified. For example, the following code installs a single handler for both key-down and key-repeat events:

EventTypeSpec    eventTypes[2];
EventHandlerUPP  handlerUPP;<?troff .Nd 10?>eventTypes[0].eventClass = kEventClassKeyboard;
eventTypes[0].eventKind  = kEventRawKeyDown;

eventTypes[1].eventClass = kEventClassKeyboard;
eventTypes[1].eventKind  = kEventRawKeyRepeat;

handlerUPP = NewEventHandlerUPP(KeyboardHandler);

InstallApplicationEventHandler (handlerUPP,
                                2, eventTypes,
                                NULL, NULL);

The userData parameter to InstallEventHandler is a pointer to an arbitrary data value. As we’ve already seen, any value you supply for this parameter will later be passed back to your handler function each time it’s called. You can use this parameter for any purpose that makes sense to your program.

Finally, handlerRef is an output parameter that returns an event handler reference, an opaque structure representing the new event handler. The handler reference is needed as a parameter to such Carbon Event Manager functions as AddEventTypesToHandler and RemoveEventTypesFromHandler, for dynamically changing the event types to which a handler applies, and RemoveEventHandler, for deinstalling it. If you’re not going to be using any of these operations, you can simply pass NULL for the handlerRef parameter, indicating that no handler reference should be returned. In particular, the handler will be disposed of automatically when you dispose of the target object it’s associated with, so there’s no need to call RemoveEventHandler explicitly unless for some reason you want to deinstall the handler while the underlying target object still exists.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.181.252