Mac OS • 38:44
This session covers in-depth High Level Toolbox topics such as window buffering, advanced window positioning and layering, Window Manager Port replacement functionality, dialog item manipulation APIs, Unicode support, custom defprocs, and more. If you're a Carbon developer who uses, or wants to use, the toolbox in any of these ways, you need to view this session.
Speaker: Pete Gontier
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Good morning! Alright. Wow, the home stretch, the last day. Well, as you can see, again, I always keep saying this, but Mac OS X, Carbon, the theme of this week. We are so excited about how much progress we've made, and how much progress we've made helping you get on the platform as fast as possible.
This session about Carbon Enhancements II to the High Level Toolbox is going to go further into depth about ways that your process of carbonizing your application is affected. It's really good stuff to know, so without delay, I'm just going to bring up Pete Gontier from the High Level Toolbox team. Please welcome Pete.
So, I hope you had a chance to check out the Carbon Enhancements I session. This is going to be a session very similar in theme. We've been doing so much work on, not only on Aqua, but on the toolbox in general, that we had to do two sessions to fit it all in.
This session will have basically the same structure as the last session. We're going to do a big section on architectural changes, which are things that you pretty much have to do in order to come up on Carbon. And the ones that we'll talk about in this session are the fact that you can no longer draw directly the Window Manager Port, There's a small change to the way control indicator action procedures are called.
There are some changes you will need to make to control and window definitions. Not anything too radical. There is a change in the way you need to instantiate custom control window and other definitions. And then of course there are some new dialog item list accessors that you'll want to use because of dialog opacity.
Then we'll move on to the stuff that you don't have to do, but the stuff that you'll probably want to do as soon as you hear about it. There's Unicode support in the toolbox everywhere. We've added drag and drop support for controls. Then we've added the usual collection of new Control Manager APIs.
And then finally, we'll talk a little bit about Carbon Enhancments in the Toolbox. Not too much, because there were two whole sessions on Carbon events, Sessions 121 and 122, and those are already passed, but I wanted to mention them just to emphasize the fact that if you get a chance to pull down webcasts or videotapes, you definitely should prioritize 121 and 122, because Carbon events are really important.
So let's talk about the Window Manager Port. Mac OS X is, as you've heard, a preemptive operating system, and the screen buffer is basically a huge global shared resource. And for performance reasons, we have not yet solved the problem of letting you draw to it in the general case. It would be a mess if we didn't control the access to it, and controlling the access to it is actually more difficult than it sounds. So we're still working on that issue.
Now you may find through experimentation that you actually can kind of sort of draw to what seems to be the Window Manager Port under DP4. And hopefully that will only happen by accident because you don't, definitely don't want to have code that expects to draw to the Window Manager Port on purpose because soon after DP4 we're going to be making some changes that will make it basically impossible. But the news isn't all bad. We have provided a few replacement services that cover the common cases that most people needed to draw to the Window Manager Port for.
The first of those cases is custom window dragging. Instead of rolling your own window dragging code, we really need you to use the DragWindow system call now. It's basically, not only do you get live dragging, which is a nice, whizzy effect for users, but it's the, excuse me, DragWindow is now sufficiently customizable that you really shouldn't have any problem figuring out how to get the constraining and gridding and the other custom behavior that you need. And this is part of the whole Carbon Events thing.
Basically, we send you Carbon Events during window dragging, and you can decide what to do with them. We send you such things as the rectangle the window would be in if you were to not change it, and then you have the choice of changing it or not, so that you get the little gridding effect that you need. I believe in the Carbon Events session there was a demo where we showed sort of docking behavior during window dragging, where windows were docked against the edge of a screen and docked against each other.
And I guess I should also point out that dragWindow now supports layering in the sense that the outline that's dragged around the-- excuse me, the window that's dragged around the screen is clipped according to the windows that are actually in front of it, so you don't have to override dragWindow just to make it look decent.
The next case is Grow Window. Basically, if you use the system's Grow Window service, that enables us to provide live growing, which is basically the window contents redraw as the user grows the window. So if the window becomes bigger, the image in the window can be drawn by the system, more or less interactively. And of course, there are the usual ways to customize window growing, again, mostly based on Carbon events.
There's one method you can use by overwriting a window definition message, and that is, of course, the get_grow_image_region message. And the reason we mention this, even though it's not a Carbon Events sort of facility, is that it's basically your only option if you need to do this for CarbonLib 102, or 102 through 104, I guess I should say.
[Transcript missing]
And then of course the best method would be to override the window size change Carbon event. And the difference there is that one of these messages hands you a region and one of them hands you a rectangle. So if you really, really, really need to change the visual appearance of the growing process, then you're probably going to get stuck overriding the region version. But it's much better if you stick with the rectangle version because I believe if you override the region version that prevents us from doing the live growing.
So some developers were rolling their own dragging, and it would draw into the Window Manager Port. And the primary reason they were rolling their own dragging was that the drag manager wasn't versatile enough. So what we did was we added a function, excuse me, an API to change an arbitrary drag reference so that you can turn off the zoom back animation for an unsuccessful drag.
And of course this API, Change Drag Behaviors, can expand in the future for us to add more flags that you can turn on or off on a drag-by-drag basis. And basically the way you would use this facility is you would create a drag reference, just like you would for any other interaction with the drag manager, and turn off its zoom back animation, and then either add no flavors and maintain all the state within your application, or add some flavors that tell you what to do and mark them all sender-only flavors so that you don't get windows in other applications highlighting it as if they're going to accept the drag.
And so, the way we sort of, the usage scenario that we sort of entertained was people who need to drag out of a window, some object out of a window, and then let the user more or less drop it in the middle of nowhere and have something appear there where there used to be nothing.
So, the last scenario we have a good explanation for is people who need to sample the pixels on the screen. And of course, you can still sample pixels right out of your own windows because we have a per window buffer that you'll be sampling out of. The problem case is sampling pixels that are outside any of your windows. In order to work well with the preemptive window manager that underlies the Carbon window manager, we have a very window-centric outlook, and we're working on ways to overcome that. But basically, let me think here.
Basically, what we think is going to happen is that there will probably never be a platform independent way to peek at pixels outside of your windows. And I guess I'll talk a little bit more about that on the next slide. But once again, we have the caveat of there are things that work in DP4 that you can discover on your own.
And in this case, you can create a port and then sort of look at its Pixmap handle and sample pixels out of it that way. And that's one of those things that very soon after DP4 we're going to change so that it doesn't work anymore. And then the final thing to keep in mind, of course, is rather than grabbing video RAM directly, you should definitely use copy bits for a number of reasons.
So the plain old drawing case is the most general case, and of course there's not a lot of sort of semantic opportunity for us to provide other services when you consider the problem in such a general way. Sometimes developers need to draw directly to the screen, or sometimes developers draw directly to the screen, just because they want to take over the whole screen. And of course you can still do that, but you've got to do a little work ahead of time in order to make that happen.
The basic idea is to create a window that covers the whole screen. And you can do that on your own by hiding the menu bar, or you can do that by asking QuickTime or GamesProcess to do it for you. And I sort of tend towards asking QuickTime or GamesProcess to take care of it for you, because they also know about things like hiding the control strip. And I imagine that eventually they'll be wired up to hide the dock as well, so you don't have to worry about all those extra details.
Um, and then of course the obvious alternative is to start thinking about, uh, ways to provide the same functionality that your application was providing by drawing directly to the screen in some other way. And we realize that probably involves thinking about a little bit of a redesign. Um... But there's not really an alternative we have to present today to solve that problem. And as I said, there may eventually be some kind of platform-dependent way to do this. Right now there's not, but we expect there probably won't be a platform-independent way. We're still looking at this problem, but we're not able to commit to anything right now.
So, control indicator action procedures have changed a little bit, and it all has to do with performance. It used to be the case that the control manager would call your control indicator action procedure basically as fast as it could. And so if your proc needed idle time, all you had to do was wait for some of these excessive calls, and call movies task, or whatever else it was that you needed to call at idle time.
And in order to provide more blocking behavior to allow other processes to have more processor, the Control Manager will now only call your Control Indicator Action proc when the mouse actually moves. So if you need idle time at this time, you should install a timer. And again, the Carbon Events session went into detail about timers. But basically, a timer is a callback that gets called at various times by the toolbox.
And one of those times is during the, during control drag tracking, or I should say, control tracking. And you can pretty much do whatever you like inside that timer. It's not a sort of a time manager sort of situation where you're at quasi-interrupt time. So definitely look at adopting timers in order to get idle time during control tracking.
So, some of the definition functions in the toolbox are changing a little bit. For the most part, you're probably not going to notice a whole lot of difference. We've done a fair amount of work building compatibility shims into the Control Manager and the Window Manager so that old control definitions continue to work, even though, in general, definitions are Carbon event driven now.
You can now write a Carbon event driven control definition or a window definition, but you don't absolutely have to. And, of course, you can't if you need to ship onto CarbonLib 104. Control definitions now take an initialized message, and the initialized message, one of the parameters to the initialized message is a Collection Manager collection.
And this sort of replaces all of the nasty overriding that you might have had to do in the past. You can basically associate data with tags in a collection manager collection, and then the control definition can pull the data out of that collection, and the tags will actually make sense instead of being the minimum and the maximum and the refcon, and they really mean something else.
Control initialization messages also can return an OS status so that if something goes wrong during the creation of the control, the definition can actually report what went wrong instead of just mindlessly fail. And this will allow, ultimately, the application to figure out, okay, not only did something go wrong, but here's a hint as to what went wrong, so maybe you can work around it or present an error to the user and let the user work around it.
The region calculation messages for control definitions have a long, nasty history. For a number of years they were what we would call 32-bit dirty, in that they sort of overrode some pointer values and set some flags in the high bits. We used to, for compatibility, preserve support for those messages in certain cases, but in Carbon it just doesn't make any sense anymore.
So we've gotten rid of those control region messages. What we'd like you to respond to instead now is a message called "Get Region," which has been around for a while. It's basically a query to you to provide a particular region, and then the control manager decides how to associate that region. that region with your control.
With window definitions, the story is pretty much the same. We have a Carbon event driven model for window definitions with a compatibility shim for old messages. And there are some changes to the old messages as well. There's also, there's a window message get region, which is, works pretty much the same. We ask you for a specific part of the window and you return to us a region and then we decide how to associate that region with the window.
WCalcRegions is pretty much gone away because it, well, it was not only not, it was not only insufficiently versatile, but basically required you to poke around inside the window record. And since we want the window record to be opaque, even to window definitions, that simply wasn't a workable model anymore.
And then of course, as I said, there's no drawing into the Window Manager Port. You would expect, if anything would draw directly into the Window Manager Port, it would be a window definition. But we can't even allow it for window definitions. So what we've done is we've made sure that when your window definition is called, the current port is already set up to something it can draw into. So you don't need to explicitly get the Window Manager Port. You can just assume that the port is set correctly when you're called.
So even if you don't write a custom control definition, you still often need to instantiate them. And basically, we've added some functions which allow you to more easily create custom toolbox objects. And they're basically analogs between the old classic APIs and the new APIs. You're not required to use the new APIs in all cases. The old APIs are still there, but for the custom definition case, you probably want to use the new APIs.
[Transcript missing]
And it is definitely the case that putting control definitions and resources is no longer the right thing to do. However, that doesn't mean you have to throw out all of your client code. Basically, we have a map which maps your old resource IDs ultimately to control definition functions. So if you have a CNTL resource, for example, which specifies that control definition, excuse me, which specifies control proc ID 2064, that ultimately maps to control definition resource ID of 129.
And then the Control Manager has a map that knows to map 129 to your function, or actually to your control def spec. And I call this a sort of a virtual resource ID because the CNTL resource thinks there's a control definition which has a certain resource ID, but in fact that control definition doesn't exist as a resource, it only exists as a map entry. And the Control Manager knows that when somebody asks for that control definition resource ID, It should relate it back to your control def spec, which you registered earlier. So here's the function you use to register a control definition.
Excuse me. Yes, okay, at the bottom of the screen. Let's just start with the beginning. Basically, when you register a control definition, you not only provide a control def spec, and a C def, or I should say a virtual C def resource ID, but you also provide a callback which allows you to translate the fields that were in the CNTL resource into either, excuse me, into a collection so that the control definition, when its initialized message gets called, it can extract parameters from the collection in a tagged format instead of knowing about all those weird, overridden, semantically confusing parameters that you used to have to deal with.
And that mechanism is obviously still needs to be there because CNTL resources are in a certain format and they have certain fields, and we can't really make that go away. So at some level, you do have to know that, for example, maximum and minimum fields really mean something else. But at least your control definition doesn't really have to know. And if you play your cards right, your client code, for the most part, doesn't have to know.
So yet another instance in the series of toolbox object opacity is that dialog item lists are, of course, opaque. And when we analyzed what kind of problems that was going to cause developers, we found a couple of cases where developers were actually pulling individual items out of a dialog item list or inserting new ones into a dialog item list. And so we've got now two APIs which allow you to do this in a controlled, supportable way.
And I guess the only sort of non-obvious part of these APIs is the second dialog item index parameter to remove dialog items. You're not forced to just remove one, you can remove two or three in the middle of the list if you feel like it. Now we're getting into the section of stuff that you may want to do once you find out about it.
Basically, all the toolbox objects now support UniCode, and they support it not only in the APIs, but also internally. We still have the Pascal String APIs, but basically what they do is create a CF StringRef, which is the object in core foundation which supports UniCode as well as several other encodings internally. And then we, from there on, the string stays CFString forever.
If you use CFString APIs, you can pretty much be guaranteed that if you-- the string that you set into a control will come back with 100% round-trip fidelity. If you use the Pascal String APIs, then we may or may not be forced to do some conversion, and you might lose a little bit of fidelity. So that's one reason you might want to adopt the CFString APIs. There's no, you know, there's no sort of-- There's no requirement that you adopt them, but over the long run it's probably in your best interest.
Internally, we're working with CFString completely, so we're not just sort of paying lip service to the Unicode thing. It's really down into the core of the toolbox. Finally, I want to talk about CFStringRef semantics just a little bit in brief. Core Foundation introduces this new concept of retain-release semantics, and we of course support those. And what that means is, in some cases, we will not actually make a copy of the string from the toolbox object. We may, effectively, increment the ref count of that string instead of making an actual byte copy of it.
And basically, you don't need to worry too much about that, except that we wanted to bring it up to point out that that is, in some cases, will be a performance win. And you should probably read up on CFStringRef and retain-release semantics to understand that fully. Now, in appearance, I think in Mac OS 8, we introduced a new API to deactivate a control.
But usually when developers need to disable a control, they resort to highlight control, which is another one of these functions with overloaded semantics. So now we've introduced a function called disable-- an explicit function we call disable control. And not only is there sort of a semantic cleanup associated with this control, but it allows us to do some things in Aqua which we weren't really able to do before.
Basically under Platinum, a disabled control looks pretty much like a deactivated control, but under Aqua, they look a little bit different. I don't know if you can read, maybe you can, the labels under each of these button images. So I'll just read them out so we're clear. The one on the far left is active.
Meaning it's basically in the frontmost window and the user expects to interact with, excuse me, it's in a window that the user expects to interact with, but it's disabled. The second one over from the left is active and enabled, so it's not only in a window the user expects to interact with, but that button actually responds to clicks. The third one over from the left is inactive, meaning it's probably in a background window, and it's disabled, so it's sort of the most colorless.
And then the one on the right is inactive and enabled, and that just means it's in some background window, but if it were in a foreground window, it would be clickable. Now, we've added some contextual menu manager support to controls. And basically that support is intended to be provided by the control definition. So the control definition is in the best position to know what contextual menu to provide. So we let it provide one.
And basically, The way that the application level client code works is you call handle control contextual menu click and it will query the control definition to find out whether it wants to provide a contextual menu. If it doesn't want to provide a contextual menu, it will, excuse me, the control manager will ultimately set the menu displayed parameter to false.
And then you can test that after calling handle control contextual menu click. And if it is false, then it means if your application knows something about that control or the enclosing window or something along those lines, then it can provide its own contextual menu. We've added control, excuse me, we've added drag and drop support to the control manager. We've got a few slides on this because it's a pretty comprehensive support.
Now, the first point here where it says, by default, controls do not track drags, basically what that means is there are some controls which support drag and drop inherently, and that support does not change. But you can tell an individual control whether, regardless of whether it supports drag and drop, you can tell it whether it should or not, so it's really up to you.
Some controls will call Set Control Drag Tracking Enabled on themselves as they're created. The only control that does this right now is the Data Browser control. And I believe there's a way to specify that it not do that. But in any case, you can turn it off. And of course there are APIs to enable or disable the capability. And I think they're pretty straightforward. There's a setter and a getter and there's, um... Not really much confusion there.
So the way drag tracking works is, or at least in one of the cases, is you install your normal drag tracking handlers, and while drag tracking is occurring, your application needs to maintain state so that it knows which of the drag tracking messages to pass to the control.
And, of course, as the mouse enters a control, you send it the enter control message, and as the mouse is moving around within the control, you send it the tracking in control message, and I'm guessing you can figure out what the leave control message is for. But anyway, the important message here is that your app needs to maintain the state during drag tracking so that controls are given the right clues to draw the user feedback correctly.
And of course, when you receive, or when the user releases the mouse and the drag manager tells you that the user is intending to drop, you basically pass the drag reference off to the control definition. It looks at the drag reference, figures out what data it cares about and what data it doesn't care about, and then incorporates that information into the control.
And in a way, this is sort of like calling set control data, except that you get to pass many different flavors, perhaps many different items. So it's sort of like, it's almost like a container being shipped off to the control, and the control incorporates whatever data it cares about. Now there's a much easier way to get support for drag and drop in your application, and basically it involves telling the control manager for a given window, just handle tracking.
You don't need to install tracking handlers. You don't need to install drop handlers. Everything is taken care of for you automatically. And you give up a certain amount of control over the process, but we think the majority case, people will just simply turn it on and forget about it. And from then on, their controls will just work. We're really proud of this API because it is definitely the longest function name in the entire Mac OS API.
[Transcript missing]
It's basically one of these Carbon event driven things, for the most part. If you need to add support for it in your control, you need to add a Carbon event handler and we'll tell you when to do the scroll to here thing.
Now of course, like the rest of the Carbon event support in control definitions, we have a shim for the old messages, but there are some conditions under which we can't do the shim translation sort of thing. The way to stay out of the situation where we can't support scroll to here for your scroll bars or sliders, the simplest way anyway, is to instantiate the live controlling variant, excuse me, the live tracking variants.
Another way to do it is if you, for some reason, need to have non-live tracking variants, specify no action proc for those scroll bars or sliders. Really what you need to stay away from is Dry Gray Region UPP. And that's a weird historical sort of... I don't want to call it an accident, but basically it's, once again, it's one of those weird overloaded semantic cases where sometimes you specify an action proc with a certain prototype, and sometimes you specify an action proc with another prototype, and it's really hard for the Carbon Event shim to figure out which situation you're in.
So basically, it eliminates the possibility that it will try to call a function with the incorrect prototype by simply establishing conditions under which it won't even try to do scroll to here. So if you stay away from those situations where it's a possibility that you would have to specify drag-ray region UPP, then basically you don't have to worry too much about scroll to here, it'll just work.
Click activation is a whole new facility we've added. Basically, click activation is a way for a control to specify what kind of behavior it wants when it's been clicked on and the window it's in is not active. And again, an active window is basically any window that the user expects to interact with.
And there are four cases that a control can specify-- or I should say four behaviors that a control can specify for the situation. I think the names are pretty straightforward. Control basically either wants the window to be activated or not, and it wants the control to actually be hit or not.
And then you can specify all the combinations thereof. And so basically your application, when it detects a click in an inactive window, it can call getControlClickActivation and then find out what to do next as a result. I believe the control which supports getControlClickActivation is the Data Browser control right now, but in the future there may be more controls which actually care about this. So if you're building framework type of code, you probably want to use getControlClickActivation in the general case and not just avoid it just because you don't use Data Browser.
So here's a chunk of sample code that shows get control click activation in action. The first if case is basically, if the window is active, do what you always did. Do what you're doing now. No change is necessary. But if the window is not active, then get control click activation comes into play. And that, basically, after you find out what the control wants, you can figure out whether you need to select the window. So if the control is saying activate the window, then you just call select window.
And if the control is saying that it wants to handle a click, then again, you just call the same old code that you've always had to handle clicks and controls, and it should work just fine. So I've given you a long laundry list of features we've been adding to the toolbox, and I want to go back over some of them to I want to emphasize what's in store for you in the next few weeks or a few months or hopefully a few days. Basically, drawing to the Window Manager Port is no longer supported. It's kind of doable in DP4, but don't get used to that idea because it's going to stop being possible very soon.
There's some changes to the custom window and control definition model. There's not only changes to the way you need to instantiate them, but also changes to the way they need to work internally. And then of course there's the dialog item list accessors that we've added in order to support dialog opacity.
Then there's the Unicode support, pervasive in the toolbox. Unicode is obviously an important future direction. There's the Carbon Events support in the toolbox, and once again I want to emphasize, if you get a chance to see sessions 121 and 122 on QuickTime streaming or videotape or however it is they end up distributing these things, definitely take that chance. And then we've added a bunch of new Control Manager features, some to support the Aqua user experience and some just because they were cool.