Although Apple has carefully designed the Carbon application programming interface to ease the transition from Classic Mac OS to Mac OS X, certain modifications are still needed to make your code Carbon-compliant. This chapter discusses a variety of such issues specifically affecting PowerPlant applications. See the document Carbon Porting Guide, available from the Apple Developers' Web site at
<http://developer.apple.com/techpubs/carbon/pdf/CarbonPortingGuide.pdf>
for much more detailed information about the "Carbonization" process in general.
This chapter contains the following sections:
In Classic Mac OS, Toolbox data structures were generally transparent:
their internal fields were accessible for application programs
to read or change directly. In Carbon, most of these structures
have become opaque: their internal fields are no longer directly
accessible to client applications. Instead, Carbon provides accessor functions for reading or changing the values of these fields. For example,
an application that formerly obtained a control's owning window
by directly accessing the contrlOwner field of the control record
ownerWindow = (**theControl).contrlOwner;
must now use the Carbon accessor function GetControlOwner instead:
ownerWindow = GetControlOwner(theControl);
Apple's Carbon Porting Guide contains a complete list of the new Carbon accessor functions, as well as information on how to locate transparent references to Toolbox data structures in your application code and replace them with opaque accessor function calls.
The preprocessor symbol ACCESSOR_CALLS_ARE_FUNCTIONS controls whether Toolbox structure accesses are transparent or
opaque. You can use this symbol to maintain a common code base
for building your project to both the Carbon and Classic APIs,
by setting it to true (nonzero) in the prefix file for your Carbon build target
#define ACCESSOR_CALLS_ARE_FUNCTIONS 1
and false in the prefix file for your Classic target
#define ACCESSOR_CALLS_ARE_FUNCTIONS 0
You can then use conditional compilation to provide alternate versions of your structure accesses depending on the target, as shown in Listing 3.1.
Conditional compilation for Toolbox structure accesses:
#if ACCESSOR_CALLS_ARE_FUNCTIONS ownerWindow = GetControlOwner(theControl); #else ownerWindow = (**theControl).contrlOwner; #endif /* ACCESSOR_CALLS_ARE_FUNCTIONS */
Alternatively, you may wish to simply convert all of your Toolbox
structure accesses to use the Carbon accessor functions and redefine
the accessor functions themselves to use transparent structure
access when compiling for a Classic target. Apple's Universal
Interface header files use conditional compilation to define the
Carbon accessor functions only if the preprocessor symbol ACCESSOR_CALLS_ARE_FUNCTIONS is set to true. You can then include conditional code such as that shown in
Listing 3.2 in your own project to provide an alternate definition when the
value of this symbol is false.
Redefining a Toolbox accessor function:
#if !ACCESSOR_CALLS_ARE_FUNCTIONS
inline
WindowPtr GetControlOwner (ControlHandle theControl)
{
return (**theControl).contrlOwner;
}
#endif /* !ACCESSOR_CALLS_ARE_FUNCTIONS */
The PowerPlant utility class UTBAccessors (where TB stands for "Toolbox") uses this technique to redefine only those
accessor functions that PowerPlant uses internally within its
own source code; if your code uses any additional accessors, you'll
have to provide the alternate definitions for yourself.
The QuickDraw globals (thePort, arrow, randSeed, and so forth) no longer exist in Carbon. Any references to them
in your code should be replaced with calls to the corresponding
accessor functions from the PowerPlant utility class UQDGlobals, such as GetCurrentPort, GetArrow, and GetRandomSeed. For instance, to set the cursor to the standard arrow, you would
replace the statement
SetCursor (arrow);
UQDGlobals::GetArrow (&arrowCursor); SetCursor (*arrowCursor);
These PowerPlant accessor functions are defined conditionally
to access the QuickDraw globals either directly (for Classic targets)
or through the new Carbon accessors (for Carbon targets). For
example, Listing 3.3 shows the PowerPlant definition for the function UQDGlobals::GetArrow. Notice that the conditionalization technique shown here differs
somewhat from that used in the UTBAccessors class (Listing 3.2 above). This is because the Universal Interface headers define
the Carbon accessors for QuickDraw globals unconditionally (unlike
those for other Toolbox data structures, which are conditionalized
with ACCESSOR_CALLS_ARE_FUNCTIONS as described above). Hence instead of merely supplying the code
for the missing conditional case, the PowerPlant accessors for
QuickDraw globals wrap both cases depending on the target platform,
as determined by the value of the preprocessor symbol PP_Target_Carbon.
PowerPlant accessor for a QuickDraw global:
inline
Cursor* UQDGlobals::GetArrow (Cursor* outArrow)
{
#if PP_Target_Carbon
return ::GetQDGlobalsArrow(outArrow);
#else
*outArrow = sQDGlobals->arrow;
return outArrow;
#endif
}
Like the QuickDraw globals themselves, the QuickDraw initialization
function InitGraf, which formerly was used to initialize these globals, no longer
exists in Carbon. Most existing PowerPlant applications perform
this initialization indirectly, by passing a reference to their
QuickDraw globals area to the PowerPlant function UQDGlobals::InitializeToolbox:
UQDGlobals::InitializeToolbox(&qd);
The PowerPlant definition of this function is now overloaded with separate versions with and without the parameter, for Classic Mac OS and Carbon, respectively. Thus the initialization call above should be changed to
#if PP_Target_Classic UQDGlobals::InitializeToolbox(&qd);
#else
UQDGlobals::InitializeToolbox();
#endif /* PP_Target_Classic */
Mac OS X features a new model for event handling, known as Carbon events. This model is based on a callback mechanism similar to the one
for Apple events. In place of the traditional event loop that
polls actively for incoming events, the application installs event handler routines for responding to specific kinds of Carbon events. Then,
instead of polling for events via the Classic WaitNextEvent function, the application simply calls the Carbon function RunApplicationEventLoop. This blocks the application's execution until an event of interest
occurs, then calls back the application's designated event handler
for that type of event. Once RunApplicationEventLoop has been called, it keeps control permanently, repeatedly passing
events to the appropriate event handlers until one of them signals
termination by calling another Carbon function, QuitApplicationEventLoop.
Each Carbon event is characterized by an event class and an event type. Event types include all those familiar from the Classic Mac OS
programming model (such as mouse-down, key-down, window update,
and application suspend), as well as new ones such as window close
and process-command. These event types are categorized into classes
of related events, such as application events, mouse events, keyboard
events, window events, and menu events. (See the Universal Interfaces
header file CarbonEvents.h for a complete list of event classes
and types.)
Each event handler routine is bound to a particular event target and to one or more specific event types. Event targets include
user interface objects such as windows, menus, and controls, as
well as the application itself. The targets are organized into
a containment hierarchy: for example, a control is contained within a window, which in
turn is contained within the application. Events are initially
directed to the innermost relevant object in the hierarchy and
propagate outward until a handler routine capable of handling
the event is found. At the outermost level, the system provides
default handlers implementing the standard behavior for each type
of event. Thus an application need not provide a handler of its
own for a given event type unless it wishes to override or modify
the standard behavior in some way.
The prototype for an event handler routine is as follows:
EventHandlerResult MyEventHandler( EventHandlerCallRef inHandlerRef,
EventRef inEvent,
void* inUserData)
(The data types EventHandlerCallRef, EventRef, and EventHandlerResult are defined in the Universal Interfaces header file CarbonEvents.h.)
Parameter inEvent is an event reference to a data structure representing the event, analogous to a Classic
Mac OS event record. Like most Carbon data structures, this is
an opaque reference, meaning that you cannot access the fields
of the underlying data structure directly; instead, the Carbon
Event Manager provides accessor functions for obtaining information
about the event's characteristics, such as GetEventClass, GetEventKind, and GetEventTime.
The inUserData parameter is a pointer to an arbitrary additional data item meaningful
to your application. If you supply such an item when you install
your handler routine, the Carbon Event Manager will later pass
it back to the handler at callback time.
Finally, the handler routine's inHandlerRef parameter is a reference to the chain of event handlers above
this one in the handler hierarchy. By passing this value to the
Carbon Event Manager function CallNextEventHandler, your handler routine can pass the event on up the handler chain
for standard processing while adding any needed pre- or postprocessing
of its own. Listing 3.4 illustrates the technique.
Pre- and postprocessing with CallNextEventHandler:
EventHandlerResult MyEventHandler (
EventHandlerCallRef inHandlerRef,
EventRef inEvent,
void* inUserData)
{
EventHandlerResult myResult;
/* ...your preprocessing here... */
myResult = CallNextEventHandler (inHandlerRef, inEvent);
/* ...your postprocessing here... */
return myResult;
}
Although Carbon events are intended as a replacement for the Classic
WaitNextEvent model, it isn't necessary for an existing application to abandon
its event loop entirely. The old WaitNextEvent is still included in the Carbon interrface. If an appropriate
handler is available for a given event, the Carbon implementation
of WaitNextEvent handles the event internally by dispatching it to the handler
instead of returning; if no handler is available (and if the event
is one of the Classic types), WaitNextEvent returns the event back to the application just as it did under
Classic Mac OS. Thus an existing application can selectively install
event handlers for some, all, or none of the Carbon event types
while continuing to handle the rest in the traditional way.
To support the transition to the new event model, PowerPlant provides
a set of new classes based on Carbon events. As of PowerPlant
2.1, these new classes are still under development and are found
in the folder PowerPlant:_In Progress:_Carbon Events.
Class LEventHandler defines a C++ object representing an event handler routine, which
you install by calling its member function Install:
OSStatus Install( EventTargetRef inTarget,
EventHandlerUPP inHandlerUPP,
UInt32 inNumTypes,
const EventTypeSpec* inTypeList,
void* inUserData);
Parameter inHandlerUPP is a universal procedure pointer to the handler routine to be
installed; inTarget specifies the event target to which this handler is to be bound.
(Again, the data types EventTargetRef and EventHandlerUPP are defined in the CarbonEvents.h header file.)
The inTypeList parameter is an array of length inNumTypes giving the classes and types of events to which this handler
responds, each specified by an event type specifier of the form
struct EventTypeSpec { UInt32 eventClass;
UInt32 eventKind;
};
As discussed earlier, the final installation parameter, inUserData, is an optional item of additional data that will be passed to
your event handler each time it is called.
Listing 3.5 illustrates how to install an event handler. Although the Carbon
API includes provisions for supporting a two-button mouse, the
application in the example is designed to respond only to the
left mouse button; any events involving the right button are to
be passed to the handler routine HandleRightMouse, which will simply emit the standard system beep and otherwise
ignore the event. The Carbon API routine GetApplicationEventTarget returns an event target representing the application itself;
the handler routine is then installed for this target, with a
type list consisting of three type specifiers representing mouse-down,
mouse-up, and mouse-dragged events for the right mouse button.
(The constants kEventClassMouse, kEventRightMouseDown, kEventRightMouseUp, and kEventRightMouseDragged are all defined in the Universal Interfaces header file CarbonEvents.h.)
LEventHandler myHandler;
EventTargetRef appTarget = ::GetApplicationEventTarget();
EventHandlerUPP myHandlerUPP =
::NewEventHandlerUPP(&HandleRightMouse);
EventTypeSpec eventList[3];
...
short eventIndex;
for (eventIndex = 0; eventIndex < 3; eventIndex++)
eventList[eventIndex].eventClass = kEventClassMouse;
eventList[0].eventKind = kEventRightMouseDown;
eventList[1].eventKind = kEventRightMouseUp;
eventList[2].eventKind = kEventRightMouseDragged;
myHandler.Install (appTarget, myHandlerUPP, 3, eventList, NULL);
...
EventHandlerResult HandleRightMouse (
EventHandlerCallRef inHandlerRef,
EventRef inEvent,
void* inUserData)
{
::SysBeep();
}
In addition to the Install function, class LEventHandler also provides a variety of member functions such as Remove, AddTypes, and RemoveTypes, which are wrappers for the Carbon functions RemoveEventHandler, AddEventTypesToHandler, and RemoveEventTypesFromHandler, respectively.
The PowerPlant classes LEventHandlerFunctor and TEventHandler are subclasses of LEventHandler that allow you to specify your handler routine in alternative
ways. TEventHandler is a template class in which the handler routine is a member
function of another C++ object. The Install member function accepts slightly different parameters than the
one for LEventHandler:
OSStatus Install( EventTargetRef inTarget,
UInt32 inNumTypes,
const EventTypeSpec* inTypeList,
T* inHandlerObject,
HandlerFunc inFunc);
where T is the class to which the handler routine belongs as a member
function, specified as a parameter to the TEventHandler template. HandlerFunc is defined as a pointer to a function with the prototype
EventHandlerResult MyEventHandler( EventHandlerCallRef inHandlerRef,
EventRef inEvent);
Notice that neither the Install function nor the handler function itself has an inUserData parameter. The TEventHandler class uses this parameter internally to retrieve the original
handler object and function supplied by the application.
Similarly, LEventHandlerFunctor is a subclass of LEventHandler that allows you to provide an ordinary C function as an event
handler. The Install member function in this case has the prototype
OSStatus Install( EventTargetRef inTarget,
UInt32 inNumTypes,
const EventTypeSpec* inTypeList,
HandlerFunc inFunc);
Mac OS X uses a new printing architecture, which differs from that of the Classic Printing Manager in the following ways, among others:
print settings record and the page format record. The Carbon printing API provides compatibility functions for
converting back and forth between the old and new structures.
printing context.
sessions. (Carbon applications running under Classic Mac OS are limited
to only a single session at a time.)
All of these changes are described in detail in the document Adopting the Carbon Printing Manager, available from the Apple Developers' Web site.
Carbon actually provides two separate printing models, called
session and non-session printing, each with its own set of API functions. All of the
Classic Printing Manager functions are replaced by analogous Carbon
functions in the two models: for example, the old PrOpenDoc and PrCloseDoc are replaced by PMBeginDocument and PMEndDocument for non-session printing and PMSessionBeginDocument and PMSessionEndDocument for session printing; PrOpenPage and PrClosePage are replaced by PMBeginPage and PMEndPage or by PMSessionBeginPage and PMSessionEndPage; and so on.
To accommodate the new printing architecture, the former PowerPlant
class UPrintingMgr has been superseded by a new class, UPrinting; UPrintingMgr is now obsolete and should no longer be used. The UPrinting class supports a common external interface for all three printing
models: Classic, Carbon non-session, and Carbon session. (The
interface for the two Carbon models is a superset of that for
Classic.) The header file UPrinting.h defines this common interface,
with the implementations for the three printing models provided
in the files UClassicPrinting.cp, UCarbonPrinting.cp, and USessionPrinting.cp,
respectively. The umbrella implementation file UPrinting.cp includes one of these three underlying files, depending on the
values of the preprocessor symbols PP_Target_Carbon and PM_USE_SESSION_APIS. The standard PowerPlant printing class LPrintout has been modified to use UPrinting instead of UPrintingMgr; your existing printing code based on LPrintout should continue to work properly without modification under the
Carbon printing models.
Another new PowerPlant class, LPrintSpec (defined in the UPrinting.h header file), takes the place of the Classic print record. In
Classic targets, LPrintSpec simply serves as a wrapper for a print record; in Carbon, it
also includes member variables holding the equivalent print settings
and page format records. The LDocument member variable mPrintRecordH no longer exists; it has been replaced by a variable named mPrintSpec, holding an LPrintSpec instead of a Classic print record. If your application defines
subclasses of LDocument, any references to mPrintRecordH in the subclasses must be changed to refer to mPrintSpec instead, and to use the relevant LPrintSpec member functions where appropriate. For instance, if your subclass's
DoPrint function contains the statement
if (mPrintRecordH) { thePrintout->SetPrintRecord(mPrintRecordH);
}
thePrintout->SetPrintSpec(mPrintSpec);
The Standard File Package, which implements the Classic Mac OS
dialog boxes for opening and saving files, has been replaced in
Carbon by a new package called Navigation Services. PowerPlant's
standard dialog classes provide a common interface for calling
the file dialogs, with implementations for both the Standard File
and Navigation Services packages. The file UStandardDialogs.i
(in the Standard Dialogs subfolder within the PowerPlant folder)
defines the interface; UClassicDialogs.cp and UNavServicesDialogs.cp
contain the Standard File and Navigation Services implementations,
respectively. A third implementation, UConditionalDialogs.cp,
tests the environment at run time for the presence of Navigation
Services and calls Standard File or Navigation Services accordingly.
The preprocessor symbol PP_StdDialogs_Option selects one of these three implementations, depending on whether
its value is set to PP_StdDialogs_ClassicOnly, PP_StdDialogs_NavServicesOnly, or PP_StdDialogs_Conditional. Note, however, that only the PP_StdDialogs_NavServicesOnly option can be used in Carbon build targets; the others will compile
code that calls the old Standard File interface, which is not
defined in Carbon and will generate link errors in Carbon targets.
To support Mac OS X's preemptive scheduling model, the global scrap, used for cutting and pasting data between applications, has a new architecture in Carbon. This new scrap architecture, based on the notion of "promised flavors" of data representation, is described in the document Getting to Know the Carbon Scrap Manager, available from the Apple Developers' Web site.
The PowerPlant UScrap namespace presents a common external interface with conditionally
compiled Classic and Carbon implementations. The interface is
defined in the file UScrap.h and the implementations in UScrap.cp, both in the Utility Classes subfolder of the PowerPlant folder.
Carbon does not support the MacTCP networking package. Applications that use the PowerPlant MacTCP classes must switch to using Open Transport instead.
In Universal Interfaces 3.3.1 and 3.4, the preprocessor symbols listed in the first column of Table 3.1 are invalid for Carbon targets and must be replaced with the corresponding symbols from the second column. All PowerPlant classes use the new names, but your source code may still be using the old ones; if so, you will have to update to the new names.
Obsolete preprocessor symbols:
| Old symbol |
New symbol |
|---|---|
Note also that sources from third-party libraries used by the PowerPlant classes may still be using the old names and have to be updated. In particular, you will need to update the following third-party library if you use it:
The file MercutioAPI.h uses the old names PRAGMA_ALIGN_SUPPORTED and GENERATINGCFM in lines 30, 120, 134, and 167.