This discussion contains some general guidelines for all types
of Xtra development, as well as specific suggestions for developing
the four types of Xtras supported by the Director XDK.
It's somewhat of a grab bag, including both very general and very
specific coverage of a number of topics.
You may want to read through this information before you start
developing Xtras. You should also review the information here
as you develop your Xtras, since much of the information applies
to specific areas of Xtra development.
This XDK comes with template projects in the Examples folder for
Transition, Sprite, Scripting, and Tool Xtras. These templates
include projects for Visual C++ and Metrowerks CodeWarrior, and
template code files containing comments that briefly describe
modifications to make to create your Xtra.
There are also a number of working example projects that demonstrate
various coding features described in this documentation. One way
to begin Xtra development is to start with a working example that
implements many of the features you plan to provide, and modify
it to include your code.
Note that when modifying the template and example projects, you
should only need to change source file names--and possibly add
new source files--in the projects. If you are developing in C++,
you can change the code model in the project.
IMPORTANT: YOU MAY FREELY CHANGE SOURCE FILES IN THE
EXAMPLE PROJECTS. HOWEVER, TAKE CARE IN MODIFYING ANY PROJECT
SETTINGS. THESE PROJECTS PROVIDE A NUMBER OF STANDARD SETTINGS
REQUIRED TO MAKE XTRAS WORK CORRECTLY.
See the Readme file in the Examples folder for more details
on using the example projects.
Be sure all class IDs you define are unique; these should be generated
with the MSVC GUID program or Macromedia's GenUID app.
Windows DLLs: Make sure the library name in your .DEF files is
unique
If possible, use resources to store any strings, including your
type symbol, display name, etc. This will provide a way for some
users to resolve conflicts if there is a duplicate symbol name.
Also it will make it easier to localize your Xtra.
Unless otherwise noted, all string lengths specified include the
terminating NULL (0x00) character (ie, standard C strings). Thus,
where a string length is specified as 32, the maximum number of
useable characters in the string is actually 31.
Do not depend on globals being initialized in Xtras. The behavior
of global variables in code resources/code fragments/DLLs varies. Better yet: don't
use globals at all.
Remember that your code may be shared by multiple applications
or projectors running simultaneously. Again, this makes it very
difficult to rely on global variables on platforms that only initialize
them on a per-load basis (rather than per-instance).
Make your code re-entrant. Your Xtra may be called back as a result
of a call your xtra makes to the host application. For example,
if you post a moveable modal dialog using MacModalDialog
,
your Xtra may be called back (from within the MacModalDialog()
call) to redraw thumbnails, re-image a sprite on the stage, etc.,
if the dialog is moved. This means that the data required to perform
these operations must never be left in an inconsistent state while
a call back to the host application is being made.
In general, all interfaces you acquire through QueryInterface
should be tested before you attempt any calls to them This is
particularly crucial with regard to application-specific calls
in your registration class (supporting IMoaRegister)
.
The MoaCreate()
, MoaDestroy()
, and Register()
methods of this class may be called by any MOA application.
One approach is for your Register()
implementation
to test for the interfaces your Xtra needs from an application
by calling QueryInterface()
, and not register itself
if they aren't present.
The IMoaMmInterrogate interface is provided for initializing libraries
and allocating large blocks of memory at startup time. This process
slows application startup, so you should be certain you need it
before adding it to your Xtra. Whenever possible, use the IMoaRegister
interface to cache initialization information, since it 's only
instantiated the first time an application encounters your Xtra.
GetMemFragment()
to load Xtras. However,
the Metrowerks debugger won't properly debug code fragments loaded
with this call. However, if you add a file in the Xtras folder
named "_XtraDebugMode_", the call GetDiskFragment()
is always used to load PPC Xtras, regardless of the VM state.
This trick is intended for debugging purposes ONLY; your Xtras
should be tested without this workaround in place before shipping.)
Prior to Macintosh OS X, there was a global memory structure called "Quickdraw
globals." This global data does not exist with OS X, so the following discussion
only applies to earlier versions of the Macintosh operating system. There are
two methods to access QuickDraw globals. Here is the first method:
#include <LowMem.h>
#include <Quickdraw.h>
#define qd (*((QDGlobals *) (*((GrafPtr **) LMGetCurrentA5())
+ 1) - 1))
If you're already accessing Director's stage window's
graphics context, then the pointer to Director's QuickDraw globals
is available as part of the nativeGCInfo
which you
can get using the IMoaMmGC:GetNativeGCInfo
interface.
IMoaMmGC
interfaces are supplied to sprite and transition
Xtras at drawing time; you can also get the graphics context for
a movie's stage window by calling IMoaDrMovie::GetStageWindowGC()
.
Note that at start-up initialization time, a movie is not yet
open so you'll get an error if you try to obtain its graphics
context at that time. However, it should be available in normal
circumstances. (Always check your error codes!) Here is an example
of the second technique:
MoaMmNativeGCInfo nativeInfo;
You might have to be careful with the first technique
since support for A5 on PPC may go away in some future system
software release (then again, so might the QuickDraw globals!).
The second technique is currently Director API-specific, so it
won't work if you're writing Xtras to be used in multiple products
(for example, transitions for Director 5/Authorware 3.5).
PIMoaDrMovie pMovie = NULL;
PIMoaMmGC pGC = NULL;
QDGlobals * pQDGlobals = NULL;
HANDLE_ERR( DrPlayer_GetActiveMovie(&pMovie) );
HANDLE_ERR( pMovie->lpVtbl->GetStageWindowGC(pMovie, &pGC)
);
HANDLE_ERR( pGC->lpVtbl->GetNativeGCInfo(pGC, &nativeInfo
) );
pQDGlobals = nativeInfo.mac_window.qdGlobalsPtr;
/* Do your stuff with the QuickDraw globals. It's okay to save
it too, since it won't change during the session.
*/
done:
/* Release interfaces we obtained */
if (pGC) pGC->lpVtbl->Release(pGC);
if (pMovie) pMovie->lpVtbl->Release(pMovie);
Remember that in general, Xtras have more than one GUID. There's
a GUID for each class, so if you have a separate registration
class, you need a unique GUID for that in addition to your asset
and actor, Lingo, or tool classes. To ensure this, try running
your Xtra with every sample Xtra in the Xtras folder; you should
not get any conflict notices.
Throughout this API, you'll find cases where you create or use
instances of MOA interfaces, sets of methods for manipulating
objects, and MoaMmValues
, object-like entities that
represent specific types of values. Both MOA interfaces and MoaMmValues
use reference counting to determine when they can be removed from
memory.
There are various ways to acquire a value or interface, and the
way you acquire it determines whether or not you are responsible
for releasing it. Call the IMoaUnknown::Release()
method to remove a reference to an interface; call the IMoaMmUtils::ValueRelease()
method to remove a reference to a MoaMmValue
.
Objects acquire interfaces and values by calling methods or by
having their methods called. The "caller owns" rule
define who is responsible for releasing an interface or value.
The rule is this: The owner of an interface or value is the
object that calls the method requesting or providing it. The
owner is always responsible for releasing an interface or value
when through with it.
Here are the four cases to help clarify this rule:
IMoaUnknown::QueryInterface()
and IMoaMmPropOwner::GetProp()
. You must call IMoaUnknown::Release()
on interfaces or IMoaMmPropOwner::ValueRelease()
on values acquired by calling these methods when you are through
with them. NewXActor()
and GetProp()
in the IMoaMmXAsset
Xtra interface. Note that this
transfers ownership to the caller. That is, if you create the
interface of value within the function being called, your ownership
ends once you have passed it along. However, if you pass an interface
or value that you are holding in an instance variable, you must
call IMoaUnknown::AddRef()
or IMoaMmUtils::ValueAddRef()
to increment the reference count before passing it to the caller.
IMoaMmUtils::MacRegisterWindow()
.
When you call this method to register a window, one of the arguments
you pass is an IMoaMmMacEventHandler
interface. After
you call MacUnregisterWindow
, you are responsible
for disposing of the event handler interface. IMoaMmXAsset::SetCallback()
,
used to pass a callback interface to media asset Xtras. If your
asset wants to hold onto this interface, it must call IMoaUnknown::AddRef()
on it to assert ownership, then call IMoaUnknown::Release()
on it when finished.
IMoaCalloc
or IMoaHandle
)
is being released properly as your Xtra performs.