Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2001-106
$eventId
ID of event: wwdc2001
$eventContentId
ID of session without event part: 106
$eventShortId
Shortened ID of event: wwdc01
$year
Year of session: 2001
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC01 • Session 106

Carbon Event Manager

Mac OS • 49:32

The Carbon Event Manager is the preferred event dispatching model for Carbon applications. Using Carbon Events simplifies application design and can significantly improve performance on Mac OS X. Learn how to write a Carbon Event-based application using default event handlers as well as how to write custom handlers.

Speakers: Ed Voas, Hockey Brokenarm, Four-Eyed FrankenGuy

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Thank you. Welcome to session 106. You know, the Carbon Event Manager is one of the most important technologies that we've added to Carbon. Because it can simplify your code, make it easier to write Carbon applications, improve your performance, especially on Mac OS X, and it's the foundation for most of the improvements that we're adding to the toolbox. So here to tell you all about it is the manager of the high level toolbox team, Ed Voas.

As Mark said, Carbon Events is one of the most important technologies to learn about as an application developer. The reason for this is that it permeates everything we do in the toolbox. Every one of the toolbox managers, like the Window Manager, Menu Manager, etc., deals with Carbon Events, at least in some respect.

It is the Alpha and the Omega. So what you're going to learn about here is you're going to learn a brief overview of Carbon Events. We're going to try to talk about basic fundamental principles, how it works, what makes it go. We'll also show you how easy it is to actually build a Carbon Event-based application. and we'll also show you some examples of how you can extend the toolbox via Carbon Events.

So what Carbon Events is, is a direct dispatching model. Events are dispatched directly to objects in your application. This is in contrast to the Wait Next Event model where it's up to you to pick up an event and decide what to do with it. The Toolbox can do that for you.

And with that, the toolbox can also provide many, many default behaviors. All the things that make a Mac application behave like a Mac application. But your applications can override those default behaviors and extend it in ways that make sense for your app. And while we have a pure Carbon Event-based model, you can also use Carbon Events in your WaitNext Event-based applications. This is to allow you to actually start installing handlers and eventually get to a point where you don't need to call WaitNext Event anymore.

So why did we do this? Well, first off, if you remember the event record structure, our friend, it only has a possibility of 16 events. And the reason for this is because there's only 16 bits in an event mask. We wanted a system where we could send hundreds of events, possibly thousands of events, all different types. If you were to look in carbontevents.h right now, you'd see hundreds of events already existing.

We also wanted to unify different models that we had. We had event records for receiving events by your application. We also had DefProx for Windows controls and menus. And we also had different notification callbacks that you can install for whatever they were. But each one of these was different.

And every time we did install a new callback or define a new callback, it had a different API, you had to learn it, and is just mayhem. So what we're trying to do with Carbon Events is unify all of that into one model that's easy to learn, it's flexible, and it subsumes everything else that we've ever done.

This also makes it simple to write applications, not only because it's a simple model, learn it once and you know it, but also because, as I mentioned, the toolbox provides default behaviors. So you get to write the code that matters for your application. And lastly, we wanted an API that would encourage good performance, particularly on Mac OS X.

So I mentioned we're going to show you how to build a Carbon Event-based app. It's really simple. The easiest way to do that is with Project Builder and Interface Builder. If you were here for the last session before this one, that was with Interface Builder. That is really the star of this duo here.

And that's what allows you to throw together a Nib file and just have it run via Carbon Events. So what I'd like to do is actually bring up somebody to show you how to do that. So here's one of the engineers from the High Level Toolbox team, Guy Fulladin.

So I'm going to show you a little bit about what Ed was talking about with respect to Project Builder and Interface Builder and how they make your life really easy when you want to make a Carbon Event-based application. Project Builder supports a whole bunch of different types of applications that you can make. I'm going to start off with a nib-based Carbon application.

There we go. All right. So you run the app, and it just works. Well, on a clean system. All the basic event handling is taken care of for you. You can click on the window title bar and drag your window around. You can resize it. You can select things from the menus and you can quit the application.

The best part, though, is the fact that this all happens with virtually no code. This is just a page of code. This is all that there is in the application. All it does is it opens up the nib for the app, sets up a menu bar, creates a window, cleans up after the nib, shows the window, and runs the application event loop. And everything else is taken care of by the Carbon Event Manager. Now, let's see. I guess I'm playing with fire here.

I can open up the nib for this window or for this application. And as you can see, it's just a plain old normal blank window just like we got in the app. And I can throw some interface elements into there with Interface Builder, so that's really cool. If you got to see the session right before this, you got to see how much you can construct of a Carbon app.

I'm going to go ahead and throw some edit text fields in here, make some a little bit bigger, maybe put some static text in there. I've got a couple buttons. Maybe this is going to be an OK button. Change the name to OK, make it the default button. Go up here. Let's say we want a button that actually quits the application. I can change it to be Quit.

And I can say, you know what? Have the Quit command in you. Save this nib, go back here, and I guess if I'm lucky it'll rebuild, and I can run. And the interface just comes up and works. I wrote no extra code. This is all just working. You can type into the text fields.

You can hit Return. It flashes the OK button. I don't know if you can see that up above. And the coolest bit, since this Quit button has the Quit command associated with it, if I click in it, the Quit command is propagated to the application and the default Carbon Event Handlers just quit the application for you. So that's just the very basics of what Carbon Events and Interface Builder can do for you. But this shows that there's a lot of power in the system that you can leverage, and Ed's going to give you some more details on how you can leverage that.

All right. So back in 1984, I was still in high school, and this is the way an application worked. Essentially everything was done by the application, and the toolbox was just there for support. So the application was responsible for the event loop. The application was responsible for dispatching events. And of course the application did something with those events and their handlers.

This is the new model. The toolbox is taking a much more active role in driving an application. In this model, the application calls that run application event loop API you saw. And it takes care of dispatching the events. And all your application needs to be responsible for is the handlers. And that's where your application lives and that's where your value is added.

So as we saw, we have a lot of standard behaviors that we support in the toolbox naturally. Obviously, you can drag windows around, you can resize them. If you had a proxy icon in the window title, we would handle that automatically for you with regards to dragging it around.

We also will handle contextual menu interception for you, so you don't have to worry about, you know, control click will just tell you contextual menu, do it. And the other thing is, with this model, we could actually handle the contextual menu click ourselves if you didn't. And at that point, we could say, well, we're going to do this.

We could say, well, we always want to put up a contextual menu when this happens. And maybe we'll implement something where we start putting up a contextual menu, and then we'll send you a Carbon Event saying, all right, we're doing a contextual menu. Please fill it in. Here's the menu. So there's a lot of power here. Obviously with menus, we deal with tracking the menu bar, dealing with command keys. We deal with updating the menu bar as necessary. And with controls, we'll automatically handle all the tracking. We'll deal with focus issues and the like.

So, before I get into the real details, I want to talk in general about how events flow through the system, or at least through an application with Carbon Events. At the top of this diagram, you see the Event Queue, and it has some events in it. Below that, we have a diagram representing what the toolbox considers to be its canonical containment hierarchy. At the bottom, we have the application. It is the container of everything. Above that, we have menus and windows. So, the owners of menus and windows are the application.

and each window can own controls and each control can actually have sub-controls based on our standard control manager control hierarchy. So what happens is an event comes off the queue and then the toolbox looks at it and says, "Okay, what kind of an event is it? Where should it go?" For this example, we'll say it should go to a control that's right below it.

Once it gets into the control, it looks something like this. Each control has a target associated with it, and this is the receptor for a Carbon Event. Each target can have multiple event handlers installed on that target, and they're installed in the form of a stack. The last handler installed is topmost on the stack. The last handler installed is the first handler to get the events. That is how applications override toolbox behaviors. So in this diagram, essentially we have an application handler at the top called My Handler.

Below that we have some handler that was installed by the toolbox, possibly a standard handler. and then below that we have an Object Handler. And this can be thought of as the actual def proc that's driving this control. So we're going to go through a worst case scenario where nothing wants this event. It's unwanted. You can feel bad about it.

So, As this event goes through, essentially we look at each handler, see if it was registered for this event, and if it wasn't, we will bypass it. Or it might be that the handler receives the event but decides at this time it doesn't want to handle it. So it basically reports, "I haven't handled it." So if this happens for all the handlers, it'll just fall through. And out of the object. At that point, the toolbox decides where should it go next. And again, it uses the standard containment hierarchy to decide that. So in this case, it will go to the window next.

and then if it isn't handled there, it'll propagate out to the application. And this is where all unhandled events will end up. Now if the application doesn't handle it, it'll actually just get dropped. If you happen to be a WaitNext Event-based app, If an event is unhandled by any of your handles, it'll actually be returned to WaitNextEvent as long as it was an event that could be returned to WaitNextEvent, like a mouse click.

Okay, details. The first thing you need to know is about the Event Ref. The Event Ref is the replacement for the old Event Record structure. And in grand toolbox tradition, it's opaque. You have to use accessors to get at it. Each event is identified by a class and a kind. So a mouse down event would actually have a event class of K event class mouse. It's a mouse event. And the kind is just some sub-event of that class. In this case, mouse down.

Now, mouse down, all by its lonesome, just knowing that the mouse went down isn't very useful. Usually you want to know where it actually happened, what the modifier keys were, things like that. Maybe what button was pressed. And the way we do that is we have an extensible parameter mechanism on an event.

So all of the things I just mentioned can actually be added to the event as parameters. Each parameter can be accessed through some mnemonic name, a constant. Each parameter can also have a type. So the mouse location is actually stored as a QuickDrawPoint. So we would actually use a constant representing that type, like type QDPoint.

The important thing about an event ref is it's not just one way. Event records were always something happened. Mouse down, "Window activated", it's always past tense kind of. Event refs can actually be used in a much more active role. You can actually send an event ref someplace to get information.

The recipient of the event can actually store information in the event ref, so that after you send the event, you can extract the information out of that. One good example is hit testing in a window. As I mentioned, we're trying to replace defprox with Carbon events, and in fact, the native messaging model for windows and controls in Mac OS X is Carbon events. We actually simulate the old proc pointer based stuff on top of that.

So when we're going to do hit testing, what happens is we get the point. We actually send a hit test Carbon Event to the window. The window then turns around and says, "Okay, this part was hit." Stores it in the Carbon Event. So this way, when the sender gets the event back, they can actually see, they can extract that parameter and find out where the mouse was hit. Carbon Events are extensible. Extensible to the point that you can actually create your own. You can use maybe your application signature or whatever you want as your event class and then have your own suite of events which you can send around in your application.

The Event Queue is a little different in Mac OS X. Under Mac OS 9, or traditional Mac OS, the Event Queue is shared among all applications. On X, that's not the case. Each application gets its own Event Queue. When events come into that application via the Windows Server, they're queued up in that application Event Queue.

However, within an application, we take it one step further in that each MP Task can get its own Event Queue. If you're running an MP Task and on that MP Task you call Get Current Event Queue, you will get a private Event Queue for that MP Task. You might hand the Queue Ref for that queue to some other entity in the application, and then you can start using that to actually post events between threads using Carbon Events.

Now, you might just use MP Primitives to do messaging back and forth, but you can also use Carbon Events as it adds a much more expressive method of events. In contrast, all cooperative threads, including the main thread of the application, share one event queue. And that's the main event queue. And that is the queue that receives all the events from the Windows Server, as I mentioned.

Whenever you're waiting on an event queue for an event, this allows timers to fire. What the heck's a timer? is the replacement for the Null Event processing mechanism through WaitNextEvent. And WaitNextEvent, if you don't handle an event, you get a Null Event. I mean, if there's no event for your application, you get a Null Event. And then you decide, oh, I guess I should spend some time doing something. Timers allow you to just request time explicitly.

And as I mentioned, they only will actually execute while you're waiting on an event queue. So these aren't asynchronous with respect to your application. They're not running at interrupt level or anything like that. They're running at task level. You can allocate memory, you can draw, you can call the toolbox. They're at that level.

The advantage of a timer is that each object in your application can ask for its own share of periodic time if it needs it. And the timers can be disabled. So for example, if you have an edit text control or something which needs to blink the cursor or perform some animation, when that goes inactive, you might want to disable that. You can actually remove the timer and stop using up that time because obviously there's nothing to animate anymore.

Timers can be set to fire at intervals, every half second, every second, or they can be one-shots. You can say, "Call me back in 15 seconds." Timers can actually fire while the mouse is down. And this is important to note because, you know, obviously if you were processing a null event, you weren't processing a click, so it's a little different. And so this means timers can actually fire while you're tracking controls and while you're tracking the menu bar.

So it's something to look out for. And Mac OS X finally has a much more reliable heartbeat. Mac OS 9, because it's cooperatively scheduled, any one application can steal the processor for any amount of time. So you can't be guaranteed to get a half second timer firing. It's much more reliable on Mac OS X.

So as we saw in the example, you drive your application to run application event loop. This is the pure Carbon Event-based-- Carbon Event API, whatever I'm trying to say. This is how you run a Carbon Event-based app. Once you enter this API, you will not exit until somebody-- sometimes a toolbox, sometimes your own application-- calls quit application event loop. And while you're in there, that's what's driving your application. The toolbox then starts pulling events off the event queue, sending them to your handlers as appropriate.

Alternatively, you can continue to call WaitNext Event and while you are inside WaitNext Event, events will be dispatched to any handlers that you have installed. And again, this allows you to start installing handlers even in your wait-next-event-based application and start adopting Carbon Events without having to go and reconvert your, I mean, you know, just redo your entire application over to be a pure Carbon Event-based app.

So while we're in run application event loop, we're inside waitnext-event, we end up dispatching events. And where they go is event targets. And we saw a picture of one. Each target can have a stack of handlers inside it. and they're typically attached to toolbox objects like menus, windows, controls. We also have some special targets.

One of them is called User Focus Target. We're going to talk about that a little later. The other one is the Toolbox Dispatcher. Whenever run application event loop pulls an event off the event queue, it just sends it to the Toolbox Dispatcher. You can actually use this fact to install a handler on the Toolbox Dispatcher to catch every event as it was being processed. It does have its uses here and there.

As I mentioned, they have a stack of event handlers. And, of course, the event handler is where your application lives. This is where you receive events. It's a simple callback. and You will only receive the events that you register for. We don't support wildcarding, and that was a conscious decision with performance in mind. We don't want to start sending out events to any handler that could possibly deal with it.

When you're inside your event handler, you can choose to actually handle the event, meaning do something with it, or not handle it, meaning let it propagate to some other object or handler which does want it. The way it tells the toolbox whether it handled it or not is by its result code.

If your event handler returns event not handled error, That's a key to the toolbox to say, all right, nothing happened here, nothing to see here, please move along. So it sends the event off to some other handler. If you return any other status code, event propagation stops dead and the event is discarded.

And once we leave a target, we do propagation. We saw the diagram of our containment hierarchy. That's what we use to decide where events should go. So if an entire target doesn't handle an event, we decide which target to send it to next, and we redirect it. I mentioned that application target is where all unhandled events end up.

Now, that's not a complete rule. There are events which we will not allow to propagate beyond the target which we send it. And the best example of those are those events which replace the old defproc messages. There's no point sending a window hit test event to the application. So we only send it to that window object.

And I also mentioned-- How your result code tells the toolbox if we should propagate or not. We call that implicit propagation. You don't really need to do any action on yourself to make the event propagate other than control your result code. However, you can do what I term explicit propagation, which is you can actually say, "Alright, I have this event, but I want the toolbox to do its default processing right now because I'm going to do something after it." And you do that through an API called Call Next Event Handler.

So you would receive an event, just call through, toolbox might do some default behavior, and then you can follow up. So you can be implicit or explicit. All right, I touched on user focus. User focus is quite simply where keyboard events go. And you can get the user focus target by calling getUserFocusTarget.

Back in Mac OS 8, we introduced keyboard focus, which is great. So you have a window and you have some concept of what the focus control is in that window. Well, from an event model standpoint, that's all wonderful and all, but when we get a keyboard event, which window should we even start with? So what we've done is we've created this concept of user focus, which is the combination of the currently active document window and any focus control in that window.

If the currently active document window doesn't have a focus control, that window is considered to be the user focus. There are times, however, when you actually want to redirect it to some other window, which isn't the currently active document window. Best example would be a floating palette which accepts some sort of keyboard input. You might want to redirect it there. And you can do so with an API called Set User Focus Window. If you're using pure Carbon Events, you don't really have to even worry about that because the toolbox will manage that for you automatically.

User Focus, along with being the recipient of the keyboard events, is also the recipient of HI commands. HICommands are merely the extension of the old menu command concept we introduced in Mac OS 8.0. So a menu command is just a position-independent way of identifying a menu item, traditionally. So instead of knowing that when the user selects Undo from the Edit menu, it's Menu ID 129, Item 1, all you need to know is that the command is Undo. And it doesn't matter where it is in the menu bar. As long as you get Undo, you know what to do.

When we were doing all this Carbon Event work, though, we decided it would be really good if we could share this with controls as well. And you saw an example of that where we hooked up the quit command to a control. So the HICommand structure is actually a little struct, is the encapsulation of that. So inside these structures, it has the command ID as well as information about where the command came from.

The propagation path is a little different based on whether the command was originated from a control or from a menu. If from a control, we just follow the standard control hierarchy, starting with the actual control that originated the command. For menus, we send the command to the menu which originated the command, and if unhandled at that level, we then send it to the user focus. And that is probably what makes the best sense. For example, if you're going to select cut from the edit menu, the command should probably go right to that focus control in the currently active document window, wherever the user focus is basically.

We also use HICommands to deal with menu item enabling and disabling. We talk about this a little bit more in the Windows and Menus talk tomorrow. But essentially, whenever a menu is about to be displayed, for each item in the menu, we send out K event command update status events. This is your cue to actually do something with the command in the menu. You might want to enable it or disable it.

Maybe you want to add a check mark or change its text. Maybe undo changes from undo to undo typing. It's completely up to you. But we send this right to the user focus saying, I'm about to show this. How should it appear? When the user selects one of these menu items or clicks the control, we send out K event command process. And that's when you can deal with the command. Commands are actually really useful for doing simple inter-target messaging.

So for example, you can have a window with controls in it. And the controls in the window can all have command IDs associated with it. And the window can be listening to all the commands that the controls generate. So every time you click a checkbox or drag a slider, the window can actually pick that up and say, oh, checkbox is it. I've got to go do something.

It's also important to use the standard command IDs that we've defined in carbonevents.h whenever possible. We have a whole list of them in there right now, and we're adding more as we speak. The reason for that is we want to be able to share the menu bar between the toolbox and the application. If the toolbox has something, You know, up on screen at the time, we need to enable and disable certain menu commands, such as the Edit menu. We need to be able to find those by command ID.

There comes a time in every application's life when you have to go modal. And typically, we've done that in the past with modal dialog. So you call get new dialog, you bring up your dialog and you call modal dialog and you sit there. Until the user hits OK or Cancel or whatever. The way to do that in the Carbon Event model is to call runAppModelLoopForWindow.

So you can create any window, be it NibBase, ResourceBase, we don't care. We just put it up on screen, and then you call runAppModelLoopForWindow. Now, at some point, somebody needs to call quitAppModelLoopForWindow so that you can actually terminate from the call. It's very much like runApplicationEventLoop, where you enter it, and you will not exit it until somebody calls the appropriate quick call.

And this is probably good enough for most uses, especially if you're pure Carbon Event-based. But you might have an application which has a lot of legacy code and you can't necessarily do that. You need to drive the event model yourself or drive the event loop while this thing is modal.

So you can actually share our modality environment while driving the events yourself. And you do that with begin and end app modal state for window. And what I mean by our modal environment is that whenever we enter this modal state, we automatically take care of disabling the menu bar and then dealing with the click situation. So we don't allow clicks outside the window that happens to be modal. So by using begin and end up modal state for window, you can actually use the toolbox's canonical modality stuff. All right, we talked about performance.

There are-- A couple of ways that you can actually tune your app for Mac OS X. And I mentioned that Carbon Events help you do that. First thing is timers as opposed to using Wait Next Event timeouts. Ideally, you should have your Wait Next Event sleep time set to the maximum it can be and install timers throughout your application.

That actually goes a long way to helping toward minimizing unnecessary idle time. Another thing is tracking loops. Tracking loops written traditionally with while still down, you know, get mouse are very, very bad on 10. They make everything else go slow. So what you need to do is instead use track mouse location.

We're actually going to show you an example of that in a few minutes. Another thing is to try to use notifications whenever possible as opposed to polling. We've done a lot of stuff in the Carbon Event Model to try to send you Carbon Events when certain things happen in your application.

or sometimes outside of your application. For example, one common polling situation is Pulling the process list. I want to know when the process comes and goes. We actually have Carbon Events to tell you when that happens. Rather than you sitting there in some sort of loop or installing a timer to do this every millisecond or something, you can just say, "Hey, let me know when an app gets launched or let me know when an app quits." And if you have cases in your application where you need to do polling and we don't have a solution, please let us know because we're really trying to add as much in the realm of notifications, notification events as possible.

So we talked about how Carbon Events is the native kind of defproc model for Mac OS X and Carbon in general. Toolbox Objects is what allows us to do this. Toolbox Objects are effectively an event handler. that you can define and register with the Toolbox using Register Toolbox Object. With this, you can then, in the Window and Control Managers, call Create Custom Window or Create Custom Control, passing one of these Toolbox Object refs that you register.

And then that handler that you associated with the Toolbox object will get called to deal with all the events having to do with DefProx. This is the recommended method for doing custom controls in Windows these days. The reason for that is quite simply this is where we're spending our time. Carbon Events is like our future and it's very flexible. Carbon Events, we can add parameters and new events.

[Transcript missing]

So that was a lot of stuff. So what I'd like to do now is actually bring up Guy again to show you a more detailed demo and show you about a lot more of the power that we have. Oh, yes, power. GUY DUBROVSKY: All right, cross your fingers for me.

So the first demo I want to show has to do with what Ed mentioned about eliminating polling. Got a small application here which is based on that basic Carbon app that I showed you before. And I'm running another application that's on the system which is a CPU gauge.

This basic app has just a normal window in it with a couple of bevel buttons. One of them's good, one of them's bad. And this app is almost as small as the one I showed you before. I added one Carbon Event handler to it to override mouse tracking for the bad control.

And the reason I overrode it is because the system already does the good thing with respect to polling. The system, when it's about to track the mouse, does not sit in a spin loop. It calls track mouse location, which will block and which will make sure the CPU doesn't get used when it's not necessary. So what I did is I overrode the click handling for the bad control, and I have a while still down loop in the bad control. So if I click on it, you can actually see the CPU just pin out.

I'm not even moving the mouse, but the CPU is using up absolutely everything it possibly can. If I was trying to download something in the background, your download speed would be cut in half. You don't want to be doing this. What you really want to do is either let the toolbox do the default behavior for you, which as you can see, if I click and move around, you know, I get a little bit of CPU usage, but if I click and hold it still, I don't get any usage. So use track mouse location if you're currently doing polling loops. Now, we'll go back to Magnify, this time for real.

Magnify is an app you may have seen. I think we've shown it previous years. It is a simple application which magnifies the screen pixels that the mouse happens to be over. And we do this with a timer. Magnify updates once every so often, grabbing the pixels from the screen and displaying it in the window. And it also has a Carbon Event Handler on the window to handle window resizing. We always want the Magnify window to be square just because we're that way. So even if I move the mouse only horizontally, the window still maintains its squareness.

Let me show you. Oh, the one other thing I want to show you about Magnify is we also offer a modal preferences dialog that lets you configure various things. You can change the zoom level, turn off the little info that gets showed in there, make it float or not float. Let's make it not float for now. So let me show you a little bit of the code and how we did all that.

The first thing I want to show you is how we did the timer. Timers, very simply, are just a proc pointer that you register with us, tell us how often you want to do something, and then when your proc pointer is called, you get to do whatever you want to do. So here's our event timer. We just called it Magnify Event Loop Timer.

It receives user data, which is basically like a refcon, so if you have some global state you need to reference or some per context state you need to reference, you can receive it there. And our timer goes in there. We do some various things. We basically create a port so we can grab some screen pixels. And we copy bits it, do various things, and end up painting it into our window.

So it's one thing to write this, but you actually need to install the event-- oh, you know what? Didn't want to hide. Need to do that. The way you install an event loop timer is you create a UPP around your proc pointer. And you pass that UPP into Install Event Loop Timer.

You tell it which event loop you want to install the timer on. And typically, you're going to want to install your timers on the current event loop. And you tell it how often you want it to fire. In this case, I have this timer firing about once every 20 seconds. And that's it. The only thing you have to do after that is just run the event loop, and your timer will automatically fire.

So next I want to show you how to write a basic event handler. Like I mentioned before, we have a window handler which makes sure that our window resizing always is square. And just like an event timer, this is a simple callback proc pointer. You receive an event, a refcon, and when your callback is called, you can extract parameters from this event. In this case, our window extracts parameters.

The attributes from this particular event. I know that this handler is only registered for one event, so I can just dive right in to extracting parameters without looking at the actual event type. But if I wrote a handler to handle multiple events, you'd actually want to switch over the type of event you're receiving.

So a window bounds change event actually contains some attributes which tells you how the bounds are changing. When the window bounds change, the window might be resizing or it might just be moving, i.e. the origin is changing. We only need to do work when the size is changing. So I check the attributes to make sure we're only going to do work when we need to.

One other thing I want to point out. We have a result code that we're going to return eventually. By default, this result code is event not handled error. So if our handler ends up doing no work, that tells the event subsystem to just go and do whatever default processing you would have done.

So going back, we know that we're changing size. We get the current bounds out of the event. That's the bounds that the Window Manager figures you want to really use. But we take them out. We don't really want to use them because we want to make the bounds square.

So we go ahead and we do a little bit of effort to make the bounds square. And then we stuff the current bounds back into the event. And we set our result to no error. No error is an indication that our handler handled the event. Don't call any default handlers. Just let this event finish now.

So our handler exits, the Window Manager ends up getting the bounds that we've changed out of the event and it resizes our window to those bounds. And as you might imagine, installing one of these event handlers is also very simple. You create a UPP for that handler. Oh, I keep hitting the wrong keys. And you call installWindowEventHandler. You pass it a window. You pass it the UPP. And you pass it the list of events that you're interested in for that handler. And it just so happens that I am only interested in the K event window bounds change event.

Once you do that, your handler is installed and anytime the window manager or whoever might need to call that handler, anytime someone needs to call that handler, it will be called. The last thing that I want to go into is handling commands for menus. We just so happen to have the default preferences command associated with the preferences item for our menu.

We need to set up a handler to handle that preferences item and display the modal dialog. In my main, First thing we do is we enable the preferences command because we know we want to allow the user to select that. And next, after we do some more setup, we install an application event handler. Now as Ed mentioned, when you select a command from a menu, the first place that command goes is the menu itself. Menus don't often handle commands, and in this case our menu does not handle that command specifically.

Then the command will go to the user focus, which would be the window. Well, our window isn't particularly interested in it, so the event ends up propagating to the application. And that's where we want to handle it. And we have an application handler. Oh, you know I keep hitting that, don't I? Just as you would expect, we pull out the direct object out of the process command event, and it happens to be of type HICommand. We look to see if it's the preferences command, and if so, we do the preferences dialog.

and the Preferences Dialog is an excellent example of how you would do a modal dialog. First, we build the window via a nib. You don't have to build it via a nib. You can build it programmatically or via resources or however you want to do it. We set up a and then we install a handler under the window to handle commands that are going to be generated by the controls in that window. And that's the pref handler.

Then we position it on screen as appropriate and we call run appmodeloop for window. Now when run appmodeloop for window eventually returns, we dispose of the window, we dispose of the event handler, and then we return from the preferences function. So most of this work actually happens inside the toolbox until our pref handler gets called.

Our pref handler is going to get called for commands associated with controls in the window. Just like handling menu commands, we extract the direct object out of the command event, take a look at the command ID, and we do various things. One of the interesting things is if we see a magnification change command, we change our magnification factor and tell the actual magnify window to update itself if it needs to.

There are a couple of standard commands for dialogs, and those are khicmdok and khicmdcancel. Those have been associated with the OK and Cancel buttons in that dialog. When we see the OK button, we write out our current settings to the preferences and we call quit appmodeloop for window. That way, run appmodeloop for window will exit and we can ultimately get out of do preferences and back into the normal application processing.

So this Magnify sample code will be released, a sample code on the website, very shortly, as soon as we can get it up there. Now those are just the basic concepts for how you'd implement sort of Carbon Event handlers and Carbon Event timers, but I want to show you the true power of Carbon Events.

We have another application on here called Live Buttons. Now, Live Buttons looks a lot like one of the earlier apps I built. It's just very simple, kind of an ugly dialog. You know, we've got some text fields in here. You know, we've got some buttons. You can pretend that this was, I don't know, some kind of fine dialog. So you click search and it goes off and does something. I mean, we're not really doing anything here. We're just animating for fun. We've got an OK button, bevel button, you know, a little disclosable area, stuff like that. And this is all being run by the toolbox.

Now, the cool thing is Live Buttons is set up such that we've installed Carbon event handlers on all the controls in this window to intercept clicking and drawing. I can put it into this edit mode and our handlers that we've installed know to intercept drawing by drawing blue frames around each individual control and intercept clicking by giving you handles that you can actually resize the controls and reposition things sort of live.

You can move my button over here, resize it so the HIV designers won't like it, I'm going to move my bevel button around and do whatever I want. And as soon as you're done fiddling with it, it's live button sample, lets you turn off edit mode and you're back to normal. That's interesting, but I can't--there we go.

Now, on previous systems, this sort of functionality would only be possible if you wrote lots of extra code or patched the toolbox. But with Carbon Events, we allow you to do things like this in a clean, legal way. But Carbon Events certainly aren't limited to just demo apps. If you've got a larger document-based application, we can serve your needs too. Now this is simpler text. This is sort of a new version of simple text that we're writing.

[Transcript missing]

and wire up functionality into the font palette that just sends Carbon Events to the main window. So I can change the size over there, I don't know, or change it with the pop-up or maybe change it to bold and let's bring it back to 60 point and do stuff like that.

Now, one other kind of cool thing that we did in this sample is handling some window events. This font palette, we set it up to handle the balance change Carbon Event to automatically snap it to the edge of the window. I don't know if you can see that jumping on the big screen, but as I get close it sort of automatically snaps it there.

A lot of applications like to group their palettes together and you can override certain Carbon Events that way. But that's not the only thing you can do with the balance change event. Now, balance change events are also good for handling live resize. We decided to wire that up too. But then you can get really strange and what if we only wanted this window to be displayed on a single monitor and as the user moved it off you could actually scrunch it up in very weird ways. So you can do that too.

There's all kinds of cool things you can do with Carbon Events and they scale very well. I mean, they're great for demos obviously, but you can fit them into large scale applications. You can wire up little bits of new functionality to take advantage of new tools. You can add little toolbox features in such a way that it doesn't disrupt your existing application. So it's really cool. I'm going to hand it back over to Ed for a couple more words.

[Transcript missing]

Well, item one, start memorizing carbonevents.h. There will be a test at the end of the conference. There's a lot of stuff in there. We have a lot of documentation in the header itself to help you along and get you started. The Learning Carbon book has some examples of how to write a Carbon Event-based application. I've submitted an article to Mac Tech which goes into this stuff in way more detail than we can talk, you know, in an hour about. And just start playing with, you know, interface builder, project builder. Start building Carbon Event-based apps. It's really easy to start.

You can just, you Create a default app and start installing handles and see what you can do. I mean the little demo where you drag the window to the corner of the screen and it kind of scrunches, that took about 10 minutes to write. So, I mean this stuff is really easy to do and like I said, there's just a lot of powerful things you can do with not much code. And that means there's much more time for you to spend doing the stuff that makes your applications great. And with that, I'd like to bring out Mark Turner again to lead us through the roadmap and Q&A. Thanks.

Thank you, Ed. Wow. Those were some kick-ass demos, huh? Thank you Guy. Okay, so here we are, 6 o'clock Tuesday. The next session you should attend is tomorrow, Carbon Windows and Menus. Ed will be back to share with you some great stuff about Windows and Menus, obviously. As you can see, the next two, Controls and Appearance 1 and 2, a good pair of sessions to catch. And finally, I really recommend the Carbon Performance Tuning session coming up on Thursday.

If you attended the keynote or Avi's presentation, basically every presentation on Monday, we talked a lot about performance and how the characteristics of your app's performance vary whether you're running on 8 or 9 or Mac OS X. And so, this Carbon Performance Tuning session will give you a lot of useful information. useful information on how to tune your app for Mac OS X.