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: wwdc2000-121
$eventId
ID of event: wwdc2000
$eventContentId
ID of session without event part: 121
$eventShortId
Shortened ID of event: wwdc00
$year
Year of session: 2000
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC00 • Session 121

Carbon Event Model: Basics

Mac OS • 47:17

This session gives an overview of Carbon Events, a new event-dispatching model used in many parts of the Mac OS. Adoption of this new model can significantly improve your application's performance, as well as simplify your source.

Speaker: Ed Voas

Unlisted on Apple Developer site

Transcript

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

Ladies and gentlemen, please welcome the Mac OS X Applications Technology Manager, David Wright. Thank you. Welcome to session 121 of Carbon Events 1. We're going to do this one more time today, so it's good to have it. This is a really exciting day today in Hall 2. The technologies being presented are amazing and will increase your productivity tenfold. This next session is about Carbon Events, and I just want to hear it. How many of you want Carbon Events like yesterday? I know it's true.

When I was working with the team to prepare for this presentation, I just was like so grateful that today is here because the number one request from Carbon developers has been Carbon Events because of the productivity, code cleanup, performance gain that Carbon Events brings to Carbon applications. So, would you please give a big welcome to Ed Voas, Manager of High Level Toolbox in Apple Software Engineering Group.

So, the Carbon Event Model is without a doubt the most significant change that we've made to the Mac OS Toolbox since its inception over 15 years ago. And I'm not just saying that because I wrote it. It's actually a fact. This should change the way that you think about the toolbox from just a collection of APIs that you call in some hopefully correct order, to a model where the toolbox does a lot of the work for you. And you just tap into that event flow and override things as you like. So what you're going to learn today: There's a lot of basic concepts and terminology that we use whenever we talk about Carbon Events. You'll also learn about how events flow in Mac OS X.

And you'll also learn where Waitnext Event fits into all of this. And you'll also learn that I'm right, and Carbon Events rock! So, what is Carbon Events? Carbon Events is the event system for Carbon. This isn't something that we stuck on the side of WaitNext Event and just kind of grafted it on. This is the event system. WaitNext Event actually is built in terms of this new event system.

But, of course, you wouldn't go through all the trouble of inventing this event system just to put WaitNext Event on top. There's got to be something more to it. And, of course, there is. And what that is, is there's a completely new way of getting events into your application. This is a way where you basically install handlers on windows controls and menus, and get events directly dispatched by the toolbox.

The best part is that this new event model can be mixed with the old event model, meaning that apps that continue to call "wait next event" can still take advantage of all the features that I'll be talking about throughout the presentation. So why did we do this? Well, we wanted to make it really, really simple to write a Carbon application. In fact, it's actually possible to write a Carbon application in about seven to ten lines of code. And we do that by providing all of the default behaviors that everybody in the world has ever had to write in order to bring up a Macintosh app.

We also wanted an event system that encouraged high performance, especially on Mac OS X. We'll go into a little bit of that as we go throughout the presentation. We also have all these nutty, disparate ways of getting notifications into your application. We have event records. We have different notifications via callbacks. We also have things like defprocs for windows controls and menus. And we wanted a system that would actually unify all of those into one consistent, homogenous model.

And as a side benefit to all of this, we ended up getting a system that actually provides much better support for plug-ins. This is a model where events can actually be dispatched directly to a window that's owned by a plug-in, rather than having the host application even involved in the event flow.

So, here's a comparison of the current WaitNext Event model with the new Carbon Event model. So, with WaitNext Event, you know, it's basically your job to do everything. You pick up an event from the system and then you decide what to do with it. So you have to dispatch the events. And you typically do this by getting, say, a mouse down, calling something like FindWindow, deciding it's on, maybe the mouse went down on the window, and Based on the result of that call, then doing something interesting like dragging the window.

So as a result, you have to write all this code over and over and over. This has resulted in many people writing their own little skeleton applications, or things like that, just to get a simple app up and running. On the Carbon Event side, for a pure Carbon Event app, the main API that you would call to drive your application is called RunApplicationEventLoop. Once you're inside there, that's it. Your app is running.

And while inside there, all the events are actually dispatched by the toolbox to the appropriate handlers that make sense for the specific type of event. As a result of this, you don't have to write that boilerplate code. We've done all the work. You just do the interesting stuff.

In the WaitNext Event Model, in order to get time to do things like blink the cursor or whatever other periodic tasks your application might have, you rely on something called Null Events. When WaitNext Event didn't have anything better to give you, it would hand you a Null Event, and you would try to do something interesting with that. In the Carbon Event Model, we have a concept called Timers. You install Timers, and that's the way your application gets timed.

In the old world, the only way to actually override any aspect of what the toolbox was doing was to patch. And obviously you can't patch in Mac OS X, but what we do have is the ability to install event handlers on all of the objects that the Toolbox supports. And that is the way you override Toolbox behavior.

So here's a conceptual picture of what we're talking about. At the bottom, we have the Carbon Event Model. On top of that, we have Wait Next Event, Run Application Event Loop. And on top of that sits your application. And your application can call WaitNextEvent or run Application Event Loop, but it's also going to talk to the Event Model directly to do things like install event handlers or timers.

Now the first new thing to be aware of is something called the Event Loop. You'll also hear this referred to as a Run Loop. This isn't the event loop that's in your application. This is another construct, very low level in the system, inside Carbon. This is essentially where events arrive for your application.

Its main job is to take events from the lower level sources and turn them into a Carbon Event and put them in your Event Queue. This is implicitly run for you whenever you call an API like "wait_next_event", "run_application", "event_loop", etc. The best part is that when you call the event loop, if there are no events waiting, your application blocks, meaning you're not spending CPU time for no reason.

So, I want to just cover the way events actually flow in Mac OS X. So, first, some event comes in, because somebody clicked the mouse, so it comes in to some I/O Kit driver in the kernel. The kernel just ends up handing that off to the Windows Server. The Windows Server is actually where the interesting part happens, because the Windows Server has the knowledge necessary to determine what app This event should go to.

So it does that by looking at what the front most process is. You know, if it was a mouse down, what window did the mouse happen to hit? And depending on what the state of things are, it'll finally send the event up into the application's event loop. And of course, the event loop just turns around, creates a Carbon Event, and puts it in the event queue. Now the interesting thing to note in this picture is that each app has its own event queue. There's not one global event queue like there was on Mac OS 9.

The other interesting thing about the event loop in general is that you need to run it in order to get the events in your queue, which is a little different from Mac OS 9, because in Mac OS 9, all these events came in asynchronously. They were typically posted through some interrupt handler.

So once you actually get the events in your application, you probably want to do something with it. The way you would drive an application is you can either call waitNextEvent like you always have, or you can switch to runApplicationEventLoop. Again, runApplicationEventLoop is really for the pure Carbon Event-based apps. You wouldn't just drop that in. I don't think it would do exactly what you wanted. But those two APIs actually dispatch events when they're called.

So while you're inside Waitnext Event or run the application event loop, events will get dispatched to any handlers that you have installed, provided the event makes sense for that handler. If you happen to need to get an event from the event system and not have any dispatching happen, there's a lower-level API called ReceiveNextEvent.

So let's take a closer look at what Waitnext Event looks like in this new system. So obviously it still works. Good thing. The biggest change is that when it's called, like I said, events get dispatched. And this means that an event, actually, if an event does get dispatched and handled by some handler that you might have installed in your application, it actually does not get returned to a WaitNext event. And this is actually the way that I mentioned that actually helps plugins. The event will actually get dispatched to some plugin, and WaitNext event just never sees it.

Another change from the traditional Mac OS point of view is that the sleep time is fully respected, regardless of whether you're in the foreground or you're in the background. Typically, on traditional Mac OS 9, if your app is in the background, we respect your sleep time, but if you're in the foreground, we will just call you as often as possible with null events. So, this is an important distinction. So you might have some scrolling animation in your about box or something, and you'd be wondering, "Why is it running so slow on Mac OS X? I thought Mac OS X was fast." And the point is, you're actually sleeping, but you didn't know you were.

So another interesting thing about WaitNext Event is that WaitNext Event can actually have performance implications on Mac OS X, because it's very typical for an application to assume that they can get all this idle time and then just, you know, sleep for one tick. And if we have 20 applications running and they're all sleeping for one tick, we're really like using all the CPU and possibly for no good. It might just be you're updating, you know, polling for modifier change or something like that.

Here's a picture, and it just illustrates what happens inside WaitNext Event. What WaitNext Event does is it actually iterates through the event queue, and for each event, start at the top, and we peek at the event, and we attempt to dispatch it. If the dispatching is successful, we remove it from the queue, we go back and get another one.

If the dispatching is unsuccessful, then it's a candidate to actually be returned from WaitNextEvent. And we do the usual mask test to make sure the event was actually something that the caller wanted. If so, we pull it out of the queue, we're done. Else we loop back around.

Now I mentioned RunApplicationEventLoop is the A pure Carbon Event way to drive your application. And the reason I said a little bit ago that If you just plop this in place of "wait next event," it won't do what you expected. That's because runApplicationEventLoop, once entered, never exits until something in your application calls quitApplicationEventLoop. So it's expected that you'll just call this in your app and then your app is running. And the flow chart looks a little simpler. We just pull an event and we dispatch it.

So I mentioned that timers are the way to get time in order to do things like blink the cursor. So they can be used to do periodic tasks like that, or you can also use them as a one-shot timer. So you might want something to time out in a couple of minutes.

The best part about this is that you can actually Put the timer code with the code that wants it. You don't have to rely on some outside entity to be gracious enough to hand you some idle time. You just say, "I want idle time," and you'll get it. A good example of that is Netatext Control that wants to blink the cursor. It can install its own timer, and it will get that time to blink the cursor.

In some ways this is really similar to something like a construct in Power Plant, where you might have L Periodic, which kind of doles out idle time. The main difference, of course, is that we're actually doing this inside the event loop. And we can do it a lot more efficiently, and we will block at the appropriate times, which won't happen in something like L Periodic.

Another important distinction, something I just want to clarify, is that these timers run at task level. These are not asynchronous to your application. They're very deterministic in when they fire. and they fire when you call the event loop. So when you call wait next event, your timers will fire.

When you call run application event loop, your timers will fire. And they fire serially, one after the other. So it's very well understood when these things are going to fire. However, there are also calls that will fire timers that you might not expect. For example, there is an API called Track Mouse Location.

And we're actually going to see an example of this later, and especially in the next session. But Track Mouse Location is a replacement for doing your standard mouse tracking. In order to do this, what it does is it actually blocks on the event loop. So, you might be tracking a mouse down event, but timers will be firing. And this might seem odd, but it actually leads to some pretty cool functionality.

The core event type of Carbon Events is the Event Ref. This is the replacement for the Event Record. This is your new friend. Essentially, it's an opaque data type, like most of the toolbox types, and you get at things inside this data type with accessors. If you're familiar at all with Apple Events, this is going to seem like a really familiar type, in that every event is actually identified by a class and a kind. So we might send you an event of class Mouse, but the kind is Mouse Down. We also have, you can also have an arbitrary number of parameters on any event. And you get at them via some symbolic name, and they're actually pseudo-type-safe, just like Apple Events are.

We also have retain and release semantics. And if you're familiar with this from Core Foundation terminology, you know what I'm talking about. If you have no idea what I mean, it's just fancy terminology for ref counting. We do that to handle threaded cases. The best part about EventRefs is they're not just a one-way communication idiom.

They aren't just "something happened." They can also be used to retrieve information from some entity. For example, the Window Manager might send a hit-test event to a Window Definition. The Window Definition could actually take that event, put in the part code, and then the sender of that event will get that part code once the event comes back.

Times are much more expressive in Event Riffs. Event records have times expressed in terms of ticks. Ticks are very, very coarse-grained, especially for some of the new input devices that are coming out. Things like high-resolution tablets and stuff, which really want very, very fine timestamping of events. We actually express this time in a double. So we use a floating point type to represent our time, which might seem a little odd, but the fact is doubles have very fine precision.

And though we're currently using nanosecond granularity for our event times, if we can get down some data, whatever, picosecond granularity, we don't have to change our data type, because it still can be expressed in terms of a float. And of course, events are no good unless you can do something with them. So obviously you can post them to the event queue, but the most important thing is that you can send them to objects. And that is what the Carbon Event Model is all about.

And where you send an event is an event target. This is an abstract concept. But what it is, is it's just a place to send an event. And they're normally associated with some sort of toolbox object. So if you want to send an event to a window, you get the window's event target by calling getWindowEventTarget. And each target has a stack of handlers that have been installed.

And they're a real stack, so when they get installed, they get pushed on top of other handlers that might have already been installed. So the topmost handler is basically who gets the event first. And that handler could turn around and say, "I handled it," or could decide, "No, I didn't handle it. Let somebody else deal with it." and it does that basically by returning the right OS status result.

And this is the overriding idiom. This is the way you override and customize the toolbox. Just by the mere fact that you've installed a handler means you're overriding something. You're going to get an event, and if you say you handled it, well then it's not going to fall through to the toolbox, which may do something, or it may do nothing, but you got first crack at that event, and that's the important part.

This is a conceptual picture of an event target. So here we have an event target with three handlers. The one on the bottom is for the object. So, let's use the example of a window. That object handler might be the DefProc, or the DefProc replacement in terms of Carbon Events.

On top of that, the toolbox might have installed its own handler, which provides standard behaviors, like dragging the window around. But on top of that, your application might have pushed another handler. And that handler might say, "Well, you know, I like all that drag window stuff you're doing, but I really want it to dock to the size of the screen." That's where you'd actually be able to tap in and override what was going on. So when an event comes in, it tries to go down as far as it can through that stack of handlers, and eventually yields a result. Either it was handled, or it was not handled.

If it wasn't handled, we end up trying to send it to another target in the event system. And we use our, what we term, our standard containment hierarchy of the toolbox to determine this. And the hierarchy is largely defined by windows and controls and sub-controls. We rely on control embedding for a lot of this.

And events are automatically sent to the innermost object possible. If you click on a button, the button will get the click first, then the window, and then it will fall through to the application. The application is like the catchall for every event that we send out. And this is just a picture of that containment hierarchy. It's pretty standard. Shouldn't surprise you.

When we send an event out, it may or may not be processed. So if the toolbox gets a click, we may send it out to some specific object and it may not be handled. Well, we may decide Well, we kind of know what it is, so we're going to send out another event which has a little bit more semantic meaning.

So, what started as a click could cascade into something really meaningful like "Window Moved." And in general, you want to listen to the higher level events rather than the lower level events because it means you didn't have to do all the code necessary to actually drag the window around.

So, if we take a visual example of that, say a click comes into an application, and we send it out, nobody responds to it, we feel bad, but we turn around and we take that, and we actually say, "Oh, you know what? That was on the Zoom box.

Let's tell people it was on the Zoom box." So we send out another event saying the Zoom box was hit, and nobody responds. We still feel bad, but anyways, we go on and we say, Oh, we know what to do when that happens. We'll just track the widget. Oh, it was a successful trap, a successful tracking, whatever. So, let's just broadcast out that the window needs to be zoomed.

and if nobody cares about that, we'll just zoom the window and say, "Hey, the window was zoomed." Now the benefit of all of this is that you can actually tap in at any one of these levels and intercept any one of these events to get the level of functionality that you want.

You might not care about any of this stuff. Maybe you don't even care that the window is zoomed. You know, you want to support that feature, but you don't want to deal with the code necessary to drive that. We can take care of that. Maybe you really have a problem with the way the toolbox does zooming. I can't imagine that, but if you do, you can just override that part of it. It's totally up to you.

So we have a lot of events that we've defined. We used to be limited by the size of the Event Mask. The Event Mask was limited to 16 events, because the Event Mask was 16 bits wide. Kind of limiting. In the Carbon Event Model, we have lots of events. We went nuts.

Some of these events you already know. You know things like mouse down, you know things like window update, window activate. But we also introduced a whole new set of events, things like control hit, process a command please, or the window was moved or it zoomed or whatever. I want to talk about a couple of specific events that have changed, or maybe are a little better.

Maybe I should have shown that later. So the first one, I want to talk about mouse events. We now actually have multi-button mouse support. If the system tells us that the right mouse button was hit, we'll tell you. I mean, we have no problem. We just give you the information. And also, if you load up DP4, hook up your favorite USB mouse, and start moving the mouse wheel, we're going to start sending you mouse wheel events.

And if you actually look at the simple text example that is on DP4, you'll see how to handle it. It's kind of a hack, but it works. And the last thing is we have multi-click detection, so you don't necessarily need to determine that a double-click happened. We'll just tell you.

Alright, now to make you sad. Keyboard input is actually different than it was. It used to be pretty simple. You get an event, it had a key character, I mean a key code, and the actual character that was typed. What we want to do in Carbon is provide full Unicode keyboard input. So we changed things a little bit.

Now I'm going to jump to the last point, which is that if you're calling WaitNextEvent, you're still going to get keyboard events just like you're used to. Nothing has changed. However, if you're using the new event model, you need to be aware of a couple of things. First, there are two types of keyboard events.

There are the Raw Key Events, which are the ones that just basically tell you which virtual key was pressed. And then, those can actually build up into Text Input Events, and that happens through TSM. TSM is built-in. This means that your app doesn't necessarily have to become TSM-aware. It is TSM-aware if it's a Carbon application, automatically.

So the raw key events, like I said, they're pretty much just a virtual key code. But TSM actually does do a little bit of processing on that before it hands it to you. But the result of that may not be what you expect. The event may not have any character information in it. It may be part of a dead key sequence. Or it may have multiple characters in it, depending on the keyboard layout.

and TSM just uses these and processes them and talks to input methods in terms of these events. But the most interesting low-level keyboard event is the Keyboard Modifiers Changed event. With this, you don't have to poll for modifiers anymore. We'll just tell you that the modifiers changed. It's very simple, which is another reason that you don't have to poll.

So the actual Text Input Event, when it comes to you, is one or more Unicode characters. And this is the result of TSM talking to any input method that may be present or up. And finally, it'll just give you some Unichars. If you actually look in carbonevents.h, you'll see the text input events and what their names are.

And you'll notice that they look very similar to what the old TSM Apple events were. And that's because the Carbon Event version of those events is the replacement for the old TSM Apple events. This doesn't mean that we won't send those Apple events. It only means that we will only send the Apple events if nobody responded to the Carbon event.

So, in that way, we actually prefer the Carbon Event model. What advantage of... Having a text input event like this is that you can actually unify keyboard filters and paste filters into one thing by just always assuming that you might have more than one character coming in to your edit text fields.

And text input is a unique problem in that we need to know where the text input goes. If we have something like a mouse down, it's pretty evident where it goes. We just look at what window it happened to hit. With keyboard event, with a keyboard input event, we kind of need to have something tell us where the event should go. And in Mac OS 8, we had a concept of the keyboard focus for any given window. And that's fine, that's great, we love it. But we need to know which window.

So we have this concept of user focus, which is a combination of a window and any focused control in that window. If the window happens to have... well, the toolbox defines this, the user focus, to be the currently focused control in the frontmost document window. That's our definition of it. If that window happens to have no controls, or nothing has focus, then it's the window.

In order to send an event to the user focus, we have an API called Get User Focus Target. And that might actually return the target for a control or a window. But it's really not important. You just want to give the text to somebody who might care. If, for example, you need to redirect the user focus, you could do that. The best example that I can provide for this is you have flowing palettes in your application.

And you're typing in your document, and then the user clicks in a floating palette. And now you want the keyboard focus to go there. You can use Set User Focus Window to say, "That's the window that has focus now. Please send all text input there, and we'll obey that." And that would be the way that you would override it. Normally, the toolbox deals with all of this stuff, and you don't need to.

In Mac OS 8, we introduced the concept of Menu Commands, which was not a new concept, but it allowed a position-independent way of identifying your menu items. What we've done in Carbon Events is we've created these things called HI Commands, which are essentially just a wrapper around those menu commands. They basically contain the command, plus a little bit of extra information necessary in order to have the toolbox dispatch it properly. And why is that important? Well, we actually want to dispatch commands to the originator of the command first, and then send it to user focus.

So let me give you an example of why we'd want to send it to the originator first. Best example there is a plugin. A plugin might install a menu into the menu bar. And it would really love to just get those events itself. All it would need to do is install the menu, install the menu handler on that menu, and then whenever a selection was made from that menu, it would actually inform the plugin first. And if the plugin said, "I handled it," that would be it. End of processing.

Why you would want it to go to the user focus is because usually commands and menus apply to the currently selected control or field or something like that. The best example is an Edit Text field. You know, cut, copy, and paste obviously apply to the user focus. and we've predefined a whole bunch of commands if you look in Carbon events dot h uh... we have things for like quit, copy, paste, uh... a number of things and the interesting thing is that a control can actually generate a command as well and we'll actually see an example of that very shortly so short in fact that it's now so what i'd like to do is actually bring up Guy Fullerton who works on HLTV with me to show you a little demo and he's actually over there Okay, so if I can switch over to the demo machine, that would be helpful.

So as Ed mentioned earlier in the presentation, the toolbox is attempting to become a framework. It's attempting to do a lot of the work that your applications used to have to do on its own. And we want to make your lives easier and allow you guys to write a lot less code and just deal with the code that gives your app the unique functionality. And I want to show you a couple of admittedly fairly simple examples of that. The first one is a simple Hello World application. It's got one source file.

Fairly short, about a page, page and a half of code. Our main does some fairly simple stuff. We install a standard menu bar. We create a window. We install an event handler on that window. We show the window. And we call the run application event loop API that Ed mentioned earlier. And then we quit. When our event loop exits, we're going to end up being done. Our window handler looks for two events. We look for a draw content event, and we look for a window close event. We do a little bit of special processing.

I have a routine which does my window drawing. And then I have a utility function I called for my main routine called install standard menu bar. There's no event processing in this code at all. I'm never calling wait next event. I'm never fetching events. I'm never looking for clicks in the menu bar, clicks in windows, or anything like that.

And when I run the app, You get the simple app. And it does a lot of the stuff that your application normally has to do on its own. It tracks menus, you can move the window around, you can grow it, you can window shade it, and do all the stuff that you would expect a normal Mac OS X application to do, and you did it in about a page, page and a half of code.

So I want to show you a slightly more complex example. When we're doing work on the toolbox, we're doing a lot of work on user interface. And the last several months have been spent on Aqua. And we have to do a lot of checking to make sure our interface is just right.

We need to zoom in the pixels and make sure the right shadowing is going on and all this stuff. But for Mac OS X, we didn't have a tool to help us do that. So last year, we actually wrote a tool called Magnify to do that. And we thought, well, we're writing an app for the ground up. We might as well test out our Carbon event model and see how that's going. So we decided to implement it with Carbon events.

And it ended up being really, really easy to do. So once again, I just have one source file. It's a little bit longer than Hello World, probably four pages of code, maybe. One of the main pieces of code is a timer that I'd mentioned. We've got a timer that executes every so often. This is the real meat of the application right here in this one function. We have a routine which creates our window, right here. We have an event handler for that window, which only looks for one event, I believe.

And we have a command handler that we're going to end up installing on the application to handle a few special menu commands. And finally we've got our main. You know, probably only 10-12 lines of real code. We install a menu bar, we install our application handler. We jump through some hoops so our timer can do some work later, and then we create our window.

So it's not a super simple app, but as you can see, it really only did the work that matters to that application. And when I run it, we get our Magnify app, which simply magnifies the pixels underneath wherever the mouse happens to be. And Ed mentioned timers and how they fire, sometimes when you may not necessarily expect, but when it's still very powerful.

So if I happen to, oh I don't know, drag the window around, you can see that, well, the timer's still firing, it's still updating the window, it's not exactly an interesting update. But if I pull down the menus, the timer's still firing, it's still drawing into the window. And the few menu commands I wired up get sent to the application and I change the magnification level.

And the last example I'd like to show you actually ties into Interface Builder. Interface Builder is one of the new tools we're showing on Mac OS X, and we've done a lot of Carbon integration into Interface Builder. It's got a way by which you can create a Carbon application. Let's see. I can select Carbon Application Nib Base. This takes you through a little assistant to do those kinds of things, but I've already got a project set up.

So I'll go ahead and open that up. And this is what it would look like when you first create it. It's got two interesting pieces in it. It's got some source, simple main.c, probably only about 20 lines of real code. It simply loads the interface from a nib and creates a window which is also from a nib and calls run application event loop. And the nib is what defines a little bit of interface. It's fairly simple. Once again, it's just a window. And Interface Builder does this for you automatically. It creates sort of a simple one window application interface.

And if I run that-- You get what you expect: a simple one-window application interface. Once again, we do all the standard behavior for you. We track the menus, we move the window, we resize the window, we can window shade the window, and do all those things you would expect. But I want to go a little, one step further. Let's actually add some functionality to the app, and at the same time... Let's cancel that and get that out of the way.

Oh, that's not what I want. That works. Okay, we're back in the nib. He'd mentioned that you can attach commands to controls. So let's go ahead and do that. I'll drag a button out of the interface builder palette and put it in the window. And if I bring up the inspector for that button, it's got a field by which you can associate a command ID to that control.

Now there's a whole bunch of standard system commands that we offer, and our application event loop pays attention to these commands and does what you would expect. So I can go ahead and just tell that control that, "Hey, you issue the quit command." And I'll save my nib. Go back, rebuild. Should take a second. Done. Rerun.

[Transcript missing]

So, you've just seen an example where you can basically bring up an interface from some data store, in this case an interface builder nib, and have the toolbox deal with all the user interaction in that window, and then potentially just tell you about the interesting things. Well, it seems like we kind of already had something like that, and we used to call it the Dialog Manager. Everybody's friend, the Dialog Manager. Well, with all the stuff that we've been working on in Carbon Events, the Dialog Manager is effectively obsolete.

This doesn't mean it's not supported, this doesn't mean they were taking it away from you, but it does mean that you can do pretty much anything that you could have done in a dialogue with pure Carbon events now. We have things like interface builder support. We have the ability to actually reference a control by an arbitrary ID that you assign.

And we also will be supporting window modality without actually resorting to using modal dialog. The best example of where we're headed with that is if you check out the Adopting Aqua session, which is tomorrow, You'll actually see how some of our Sheet APIs rely on this modality, and Sheets are completely Carbon Event based.

I mentioned at the beginning that we're trying to unify different models. One of those models is the DefProc model. Instead of a callback-based DefProc, what we really wanted was an event-based DefProc, and we have that. In DP4, windows and controls are completely driven through events. The old DefProcs don't exist anymore.

This doesn't mean you can't create a button, of course. But Carbon Events is the native model. And we're just going to keep doing more and more of this as we go forward with the toolbox. And the beauty here is that you can override these events like you would anything else.

If you don't like the way the control draws, intercept the draw and do something cooler. I don't know. Actually, if you stick around for the second session-- that's my plug to keep you here-- we'll actually show you an example of some of the cool things that you can do with this stuff. And while we redid this stuff in Carbon Events, we took the opportunity to actually clean up the messages, get rid of some old messages that didn't make any sense.

And of course, the beauty of all of this is that def procs were really weird. It's the best word I could think of. I mean, for each different message, you needed to learn the arcane rules of what would come through the param parameter and what you should be returning. And in controls, there was this thing like test new message support and all this weird stuff. Carbon Events just make that go away.

I also mentioned at the beginning that we wanted an API that would encourage performance, specifically on Mac OS X. We do that in a number of ways. The first is timers. It is highly recommended that you use timers instead of small timeouts and wait next event. This is largely because timers are much more efficient to fire when you're already in the event loop. Restarting the event loop has a cost.

Another way to help your app perform well on Mac OS X is to adopt Track Mouse Location. The second session shows an example of that. And again, I mentioned at the beginning that this is a way to rewrite your mouse tracking loops. The traditional mouse tracking loop will actually pin the CPU on Mac OS X.

You can listen to events like the Modifiers Changed event, which is an example of an event that we're sending out now that you didn't have before, so you had to resort to polling. And in fact, if there is anything that you know of that you are polling for in your application, we absolutely want to know so that we can try our best to get an event in there instead. I mean, making it work that way is for all our benefits.

And another thing that we want to do is move to something called Tracking Areas. This isn't in there right now. I'm not sure exactly when it will be. But, right now we send your application tons of mouse-move events. Every time the mouse moves one pixel, you have an event.

Which is good in some cases, but in a lot of cases, you don't care. All you want to know is, did the mouse enter or exit this particular region of my window? Because I need to update the cursor or something. Well, Tracking Areas are a way to do that. And hopefully they'll come online soon.

So, with that, I mean there's a lot of stuff here. So much stuff that I have a second session. Did I mention my second session? So, what you should be seeing from this is that the toolbox is largely doing all the work for you these days, and we want to continue on that path. We want to make it such that when you write an application for Carbon, the only lines of code that you write are the kinds that are unique to your application. That is what it's all about.

It's also important to know that learning Carbon Events is important in that everything we do from this point forward will be in terms of Carbon Events. Carbon Events is our messaging model. Carbon Events is our event model. Carbon Events is our notification model. And once you learn that one paradigm, you've learned them all.

and I mentioned you get new toolbox features essentially for free and the way that The way that you get that is just because we can start sending out new events. And if you're really Carbon Event savvy, you're probably not looking for them because they're like new and stuff.

So we can actually send these new events, implement new functionality, and then when your app is ready, you can actually start tapping into that functionality. So we won't get into a lot of the problems that we've had in the past trying to implement functionality a la Windows Shade behind your app's back.

And the best part, the absolute best part, is that this is a gradual adoption path. There is no need for you to even rewrite one line of code just to make your app port. However, if you want to make it perform well, you might have to do a couple of things. Beyond that, you can start installing handlers on a case-by-case basis throughout your application. We're not telling you to rewrite your app. We don't want you to have to completely rewrite your app. That's the whole point of Carbon.

I don't know. This stuff is just cooler than cool. And if you're not seeing that, I don't know what to do. Can you tell I'm excited about it? In any case, what I'd like to do is have you check out carbonevents.h start looking at it. The API is really refined now.

This is one of the few projects where I've been able to actually get time to really refine the API over and over and over again. If you've actually been following Carbon Events, you know that it's changed over time. Now I think we have a really, really highly refined API that actually feels really good as well.

So please take a look. Check out all the events we have. And let us know what other events you might need. I mean, sometimes a lot of these are easy. We've had requests, for example, people want to know we want to know when the window is hidden or shown. Well, that's easy for us. We can just add an event and send it out, and then you get that notification. No need to patch.

Another simple thing you can do is right now, on your PowerBook, just pass MaxLong into your sleep time on WaitNext Event and start installing timers. They're really easy to adopt. And of course, you want lots of feedback, and you can direct your feedback to [email protected], or you can talk to your pal, Dave Wright. And I'd like to bring Dave Wright back up here so he can lead us through the roadmap and onto Q&A. Thanks.

Thanks, Ed. So, can we get all into Ed's head? This stuff is cool, no? Let's, I mean, just give it up. It's just amazing stuff. So, alright. So I just want to do a brief overview of the roadmap that kind of follows on to this kind of content. Right after this session, there's the advanced session of Carbon Events. So go ahead and get a drink and come on back.

Tomorrow, the high-level toolbox team is going to be giving a session on adopting the Aqua interface. So those are high-level toolbox changes to APIs that are specifically about Aqua. And then there'll be two other sessions, one on Thursday and one on Friday, about generic high-level toolbox changes that affect all Carbon applications. So be sure to go to those sessions to get your apps carbonized as best possible. At this time...