MPW, Carbon, and Building Classic Mac Apps on Yosemite
Steven Troughton-Smith (via Thomas Brand):
mpw is an m68k binary translator/emulator whose sole purpose is to try and emulate enough of Classic Mac OS to run MPW’s own tools directly on OS X. MPW is unique in that it provided a shell and set of commandline tools on Classic Mac OS (an OS which itself has no notion of shells or commandlines) - this makes it particularly suited to an emulation process like mpw attempts to provide, as emulating a commandline app is a lot easier than one built for UI.
[…]
With the same source file, and only a handful of #ifdefs, I could build the same app for 1984’s System 1.0 all the way up to the current release of OS X, Yosemite.
[…]
Right now [mpw is] a fully usable tool that makes Classic Mac OS compilation possible and easy to do on modern versions of OS X, without requiring emulators or ancient IDEs or the like. To my knowledge, this is the first time this has been possible (excluding legacy versions of CodeWarrior).
Update (2015-01-31): Pierre Lebeaupin:
And in fact, it is a very interesting exercise because it allows a better understanding of what makes the Macintosh the Macintosh, by seeing how it was originally programmed for. So I encourage you all to try and play with it.
[…]
QuickDraw is, by some aspects, more approachable that Quartz (e.g. no object to keep track of and deallocate at the end), but on the other hand the Carbon transition added some hoops to jump through that makes it harder to just get started with it; for instance something as basic as getting the black pattern, used to ensure your drawing is a flat color, is described in most docs as using theblack
global variable, but those docs should have been changed for Carbon: with CarbonGetQDGlobalsBlack(&blackPat);
must be used to merely get that value. Another aspect which complicates initial understanding is that pre-Carbon you would just directly cast between aWindowPtr
, (C
)GrafPtr
, offscreenGWorldPtr
, etc., but when compiling for Carbon you have to use conversion functions, for instanceGetWindowPort()
to get the port for a given window… but only for some of those conversions, the others just being done with casts, and it is hard to know at a glance which are which.