[ First ]  [ Previous ]  [ Next ]  [ Last ]  [ Manuals ]

 

Chapter 3.

 

Carbonizing Your Code



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:


Opaque Data Structures

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.


QuickDraw Globals

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);

with something like


  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 */

Carbon Events

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.


Properties of Carbon Events

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.


Event Handlers

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.


PowerPlant Classes for Carbon Events

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.)

Installing an event handler:


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);

Carbon Printing

Mac OS X uses a new printing architecture, which differs from that of the Classic Printing Manager in the following ways, among others:

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);
  }

you must change it to


  thePrintout->SetPrintSpec(mPrintSpec);

instead.


Navigation Services

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.


Carbon Scrap Manager

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.


MacTCP and Open Transport

Carbon does not support the MacTCP networking package. Applications that use the PowerPlant MacTCP classes must switch to using Open Transport instead.


Obsolete Preprocessor Symbols

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
PRAGMA_ALIGN_SUPPORTED  
PRAGMA_STRUCT_ALIGN  
GENERATINGCFM  
TARGET_RT_MAC_CFM  
CFMSYSTEMCALLS  
TARGET_RT_MAC_CFM  
GENERATINGPOWERPC  
TARGET_CPU_PPC  
GENERATING68K  
TARGET_CPU_68K  
GENERATING68881  
TARGET_RT_MAC_68881  

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.


[ First ]  [ Previous ]  [ Next ]  [ Last ]  [ Manuals ]

Visit the Metrowerks website at: http://www.metrowerks.com
For assistance contact Metrowerks Technical Support at: cw_support@metrowerks.com
Copyright © 2000, Metrowerks Corp. All rights reserved.

Last updated: August 07, 2000