• Battle for the Universe's port to OS X (progress report)
  • Screenshot of BftU on OS X (DR3)
  • Loadscreen of BftU on OS X (DR3)

  • Battle for the Universe (BftU) is a medium-sized, well-written application, and, as such, required a very reasonable effort to port to Carbon. Apple describes the porting effort for large applications as "about the same as was needed for converting 68K applications to PowerPC, and can usually be accomplished in less than two weeks." This was true for BftU, but it very well may not be true for larger applications. So without further ado, let's talk about the port.

    Apple breaks down Carbon development into three categories: API changes, Mixed Mode opacity and toolbox opacity.

    The API changes constitute the part which requires the most subjective changes to the code. Since Apple has gone to great lengths to make things easier for new developers. Many changes simply amount to the removal of boiler plate code. For example, there is no need to initialize the toolbox in Carbon. Nor is there the need to set up the Apple menu or process selections in it. Apple has also gone to great lengths to simplify common tasks. For example, no more messing with the desktop region to hide the menu bar. Now you call HideMenuBar (how simple)! The Printing Manager happens to be one area of significant change, but for those who simply rely on QuickDraw calls, the changes are pretty basic as the Classic API has obvious counterparts in the Carbon API. It's not always such a pretty picture, however. One area of difficulty, at least for BftU, was the removal of black and white ports. BftU used bitmaps for the masks of its graphics; now it uses 1-bit deep pixmaps. Dealing with bitmaps is somewhat easier than dealing with pixmaps because you can simply allocate what you need and calculate everything yourself. When dealing with pixmaps, you must make sure you have a proper GDevice set up and go through the MacOS API. Making the switch was a little bit more difficult than it ought to have been, imho. Of course if you are already using GWorlds or 1-bit deep pixmaps for your black and white maps, you shouldn't have to touch a thing.

    Mixed Mode opacity involves replacing NewRoutineDescriptor/DisposeRoutineDescriptor calls with system-supplied UPP creation and deletion functions (like NewModalFilterUPP and DisposeModalFilterUPP). This is fairly straight forward. However, I would throw defProcs into this category. A pretty standard defProc trick is the old jmp [addr] trick, where your code resource is merely a stub containing a 68K jump instruction followed by the address of a UPP for your custom definition procedure which you've filled in at runtime. Porting those are easy, as you simply register your defProc instead of setting up the stub code resource. However, BftU didn't do this trick. It used an LDEF from a separately compiled project (which would have been considered maintainable in Classic programming). BftU's LDEF was loosely based on a sample LDEF illustrated in the most excellent book, "A Fragment of Your Imagination" by Joe Zobkiw. For the curious, the LDEF draws a cell by getting a drawing routine from the cell which has been supplied by the client. Since the LDEF is 68K code, the drawing routine must be a UPP. All of this is very bad in Carbon. First of all, Carbon does not support 68K LDEFs, and even if it did, Carbon does not support creating your own custom UPPs. So, porting this LDEF to Carbon involved replacing the drawing UPP with a ProcPtr and compiling the list definition procedure as native code.

    The last category is toolbox opacity, which for most of us amounts to just a lot of monkey work. Instead of dereferencing a DialogPeek to get at the aDefItem field, you have to go through the accessor function GetDialogDefaultItem. Instead of dereferencing a CGrafPtr for its visRgn field, you must call GetPortVisibleRegion and pass in local storage to receive a copy. Instead of referencing qd for QuickDraw globals, you must call a GetQDGlobalsXYZ accessor function, again often burdened with passing in local storage to receive a copy. All of this is annoying, but worse is the stricter typechecking applied to ports, windows and dialogs. In Classic, WindowPtr and DialogPtr are typedef'd to GrafPtr. This makes it very easy to use the "wrong" type because there is no consequence. For example, BftU often stored the result of a GetNewDialog call in a WindowPtr variable. In Carbon, the three structures are not the same. If you have a dialog and you need a window, you must call GetDialogWindow. If you have a window and you need its port, you must call GetWindowPort. Apple has some convenience functions like GetDialogPort, so look for those to avoid a lot of nested calls. Apple also has functions to cast "up" in case you were wondering about that (i.e. GetWindowFromPort). Typically, you'll want to type your variable with the highest type that it qualifies for. So, for example, put the result of GetNewDialog in a DialogPtr variable. The reason for this is that on MacOS X, the routines which cast "down" are cheaper than the routines which cast "up". One last thing about toolbox opacity and we're done. It's a common trick for developers to embed a toolbox structure like a WindowRecord in their own structure. Since toolbox stuctures are opaque, this is impossible on Carbon. Updating code that does this would be a little bit more of a pain I think.

    Runtime. This was perhaps the most difficult part about the porting effort. For those of us not familiar with UNIX, you will have to learn a little bit about GDB. GDB is kind of like MacsBug. To debug your app, you must first use GDB on LaunchCFMApp (an application on MacOS X which launches CFM applications). From there you run your app. If you crash, type in "bt" (which stands for backtrace) to get a stack crawl. That's about all I know about UNIX and GDB.... However, I can go on about specific runtime issues I encountered. First, all windows on MacOS X are double-buffered, meaning that when you draw to a window, you are actually drawing to an offscreen map. The operating system takes responsability for syncronizing the offscreen map with the window on the display. However, if you do not have a normal event loop, you'll need to call QDFlushPortBuffer. It took me quite a while figuring out why my windows were all white. The other big runtime issue I had was that the KeyMap returned by GetKeys had the bit ordering reversed in each of the long words. This also took me quite some time to figure out. I don't think these problems were very difficult, but they were hard for me because my UNIX skills are so poor.

    All in all, this port wasn't so terrible, and it was very rewarding to see BftU up and running on MacOS X. I hope you found this report valuable, and I look forward to seeing everyone running on MacOS X!

    --MistySoftW@aol.com


    Screenshot of BftU on OS X (DR3)


    Loadscreen of BftU on OS X (DR3)


    Home | Battle for the Universe | OS X Screen Savers | Download | Authors