This TechNote contains useful information about writing Director (and Authorware Transition) Xtras that didn't make it into the XDK documentation.
A PowerPC MOA host application will never load a 68K Xtra. This is why all the Xtra samples are "'FAT" binaries. The best solution is to test the 68K behavior of your Xtra on a 68K Macintosh. However, developers have reported a hack to run 68K code on a PowerPC. This is unsupported. Use at your own risk.
- Make a second copy of Director.
- Using ResEdit:
- Strip out the "cfrg" resource
- Remove the datafork of your second copy of Director.
This give you a 68k version of Director running on a PowerPC.
The two versions of Director share the same MOA cache file, which will probably lead to confusion and error. Delete the Director 5.0 Xtra Cache file from System Folder:Preferences when you switch from one to the other.
If your sprite images off-screen and only changes visual state occasionally, you may want to setkMoaMmXSpriteCapsFlags_IsStatic in its spriteCaps in response to IMoaMmXSpriteActor::GetCaps(). If an off-screen sprite is static, Director doesn't call theIMoaMmXSpriteActor::CollectChanges() method and only calls upon the sprite to images when you notify it via InvalSpriteRect(), or if redraw is necessary for some other reason (handling an update event, or another sprite which intersects with yours is changing, etc.).
For the finest control, you want your sprite to be non-static so you can tell the host application specifically what region of your sprite's rect needs updating in the next frame in response toIMoaMmXSpriteActor::CollectChanges().
Your sprite actor does not have to supply a changed rectangle in response to IMoaMmXSpriteActor::CollectChanges(). If nothing changed, don't image! Director will invokeIMoaMmXSpriteActor::Image() anyway if the sprite moved, changed size, was covered up, etc.
If you are drawing to Director's offscreen buffer, you can get direct access to the pixels in memory. It's complex and OS-specific, but for ultimate performance, you can invokeIMoaMmGC::GetNativeInfo() on the graphics context passed to your Image routine, and then interpret the structure.
You are always passed the entire rectangle of your sprite inIMoaMmXSpriteActor::Image(), regardless of what you supplied to IMoaMmChangeCollector::AddChangeRect(). However, Director does establish a clip region for the part of your sprite that actually needs imaging, whether you are drawing direct-to-stage or to the offscreen buffer.
- If you use regular drawing commands, your drawing will be clipped to this area.
- If it takes you a long time to render, you can invokeIMoaMmGC::GetNativeInfo() on the graphics context passed to your Image routine, and then use O/S-specific calls to find out about the clipping region (visRgn andclipRgn on the Mac, GetClip on Windows).
- If you access the pixels directly, you must respect this clipping region.
Direct-to-stage sprite assets are called to image on every frame regardless of whether they are static or supply changed rects. This is partly by design; it allows the Xtra to get CPU time for rendering for every frame. It makes sense for video playback that changes with every frame. Director will not modify the sprite actor's region of the stage, so the sprite can choose where and whether to draw; however, if the sprite needs to repaint for other reasons, not drawing can leave garbage pixels on the stage (see next item).
Direct-to-stage sprite assets can't tell if they are called to image because of a frame step event, or because the stage window needs repainting (it changed size, was covered up and then exposed, etc.). If your sprite actor is actually a window, it will get window repaint events for these. Another approach is to optimize drawing only to draw changed areas, but to repaint the entire sprite rectangle every few frames.
- #include <LowMem.h> #include <Quickdraw.h> #define qd (*((QDGlobals *) (*((GrafPtr **) LMGetCurrentA5()) + 1) - 1))
The above will work for 68K and PowerPC Macs.
- If you're already accessing the Director stage window's graphics context, then the pointer to Director's QD 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, and you can also get the GC for a movie's stage window by callingIMoaDrMovie::GetStageWindowGC().
Note: At start-up initialization time, a movie is not yet open so you'll get an error if you try to obtain its GC at that time. However, should be available in normal circumstances. (Always check your error codes!)
MoaMmNativeGCInfo nativeInfo; 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 yer stuff with the qd globals. Ok to save it off too, since won't change during the session. */ done: /* Release interfaces we obtained */ if (pGC) pGC->lpVtbl->Release(pGC); if (pMovie) pMovie->lpVtbl->Release(pMovie);
You might have to be careful with the first technique since support for A5 on PowerPC may go away in some future system software release (then again, so might the QD 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 (i.e. transitions for Director 5/Authorware 3.5).
- the cast member type
- the platform (Macintosh or Windows)
Only the macPICT and winDIB formats are currently supported for image media.
If you are specifying macPICT format media for an image, thepMoaVoid mediaData; parameter toIMoaDrUtils::NewMediaInfo() must be of typeHandle, or more precisely a PicHandle; in other words a pointer to a pointer to a Picture structure.
All IMoaDrUtils::NewMediaInfo() does is encourage you to provide the parameters to fill in theMoaDrMediaInfo structure. TheIMoaDrCastMem::SetMedia() call will actually test.
Director tends to report an out-of-memory condition for errors in setting bitmap media, assuming that the bitmap was too large.
Although the table of media formats gives "composite" and "moaHandle" as the media label and format for Digital Video, digital video is not currently stored in a movie. Instead, Digital Video is always linked, regardless of whether you check the "Linked" box when you import it. (This information is in the online help under "Importing digital video movies".) As a result, you can't get the media of digital video cast members, you would have to read the linked file (accessible via the fileName property).
macPICT is only supported on the Mac, andwinDIB is only supported on Windows. Director 5 doesn't have the notion of a single, cross-platform image format in the MOA APIs.
When supplying a DIB to SetMedia orAttachMedia(), it must be a global handle. If you get errors, make sure you can lock the handle withGlobalLock().
Manipulating DIBs is tricky, and differs between 16-bit Windows and Win32. Microsoft provides samples with routines that manipulate DIBs in MSVC 1.5x (see Samples\ShowDIB and DIBView) and MSVC 2.0 and higher (see Samples\Win32\Wincap32).
To manipulate a PICT, typically what you do is render it into a GWorld. Create a GWorld which is the bounds, pixel depth, and palette of the PICT (you can get this info using the PICT utilities the Macintosh toolbox calls). Then call DrawPicture() to draw it into the GWorld. There, you've got direct access to the pixels, just like a DIB on Windows. Also, since it's a GWorld, you can use the normal Macintosh QuickDraw calls to draw into it and manipulate it like a GrafPort. To create a PICT again, you open a picture, CopyBits() the GWorld onto itself, and close the picture. This is somewhat cumbersome, and relatively slow, but PICT is pretty universal on the Mac and as such is the most convenient choice as an interchange format.
Everything should be uncompressed, flat pixels by the time you get it (in your GWorld on the Macintosh or DIB on Windows). The pixel depth may be 1, 2, 4, 8, 16, or 32 bits. The palette may be any palette if pixel depth is less than or equal to 8 bits.
When setting bitmap media, there are several useful options for specifying the pixel depth and palette to use; seedrtypes.h.
- Have Director remap the image for you to fit the current pixel depth/palette of the cast member you're setting.
- Have it use the pixel depth/palette from the image data.
- Specify them explicitly via an auxInfo structure, optionally having Director dither it if necessary.
- Have Director put up a dialog to let the end-user specify.
Asset Xtras have separate media data and property data, and separate methods for editing and streaming in/out each kind of data. Media data is intended for "bulk" data such as pixels, samples, etc. The MOA host application can optimize memory use so it does not always load the bulk media when it loads the Xtra. Director streams in property data immediately when your movie or cast file is opened, right after your IMoaMmXAsset instance is created, so property data are always available. But Director rarely loads the media data for a cast member when a cast member property access occurs, because it's inefficient; it takes time and RAM to load potentially bulky media. As a result, when an instance of your IMoaMmXAsset is loaded, it often will not have its media data available. There are flags to specify that media needs to be available, but in Director 5 there is no API you can call to force your media to be loaded.
This can cause problems if you implementIMoaMmXAsset::Get/SetProp() to get or set information that depends on media data as well as properties data.
What you can do is simply cache values in your properties chunk for properties that depend on your media. For example, if you wanted to create a bitmap-style Asset Xtra, and you had a "colorDepth" property, then you could store off the color depth of your bitmap in your properties data so it is accessible without the media being around. In this approach, setting media-dependent properties is a little trickier. You'd have to store off the new value, along with some flag indicating that it has changed (ie, your media needs updating). Next time your media is loaded, you check to see if it's invalid (due to any pending property changes), and if so, fix it, and mark your media dirty (and clear your internal change flags) so that your media data get written out on the next save.
Another workaround for Director only is to try to useIMoaDrMovie::CallHandler() withpreloadcast. If there's room in memory for your cast member, and you're not loaded yet, you'll be called back immediately to load yourself. If there isn't enough RAM, it may fail, and you won't get called back. This is a good reason to cache values in your properties data: Users can access them without the time and RAM overhead of loading your media data; and when Director is unable to load your media data for some reason (not enough RAM; missing an external (linked) file, etc.).
Director has an Xtras menu in authoring mode. Director places various items from the Xtras folders in this menu.
Tool Xtras appear in the Xtras menu automatically, displaying using the UI name and category (organized as a submenu of Xtras) that you register. You could supply an "About" box for any kind of Xtra this way.
Director will display movies and casts that it finds in its Xtras folder in the Xtras menu. This is how the AnimWiz MIAW and Palettes cast work -- they are not MOA code, but Director recognizes their type and puts them in its Xtras menu. You could supply a sample movie or user interface that manipulates your Lingo or sprite Xtra this way.
You can combine the two approaches to provide a sample movie in a submenu. Movies and casts don't ordinarily appear in a submenu, but you can create a simple Tool Xtra that loads a related movie by sending Lingo commands withIMoaDrPlayer::CallHandler(). Your movie will then be grouped in your submenu of the Xtras menu. You can still keep the movie in a subdirectory within the Xtras folder by giving it a name starting with "-". It seems that Director will not display movie names that start with "-" in its Xtras menu.
Be aware that not all products implement all kinds of Xtras, so don't depend on all kinds being loaded. Future products might, for example, support Lingo Xtras but not Tool Xtras.
You can use Xtras with Shockwave for Director 5.0. As with Projectors, (see Providing Xtras in Appendix A ofLearning Lingo), Xtras aren't packaged with Shockwave. The user must acquire the Xtra and put it in a special Shockwave "support" folder. Shockwave loads all Xtras, XObjects & linked media from this folder.
For Netscape on the Mac, in Shockwave for Director 5.0, the support folder's name is the same as the plug-in name, with "folder" appended.
For Netscape on Windows, the support folder's name is the same as the plug-in name with the .DLL extension removed. The 32-bit plug-in name is NPDSW32.DLL, so Xtras go in Netscape path\pluginS\NPDSW32\. This is the same place where M5DRIVER.EXE and other support files are located.
For Microsoft Internet Explorer on Windows 95, the support folder is currently the entire \Windows\System directory.
In all cases, the host MOA application traverses subdirectories of the support folder, up to four levels deep. So you can place Xtras in folders. It is a particularly good idea to put Xtras for Microsoft Internet Explorer in a\Windows\System\Xtras\... folder.
The MOA host application detects and registers Xtras that it finds in its Xtras folder(s) at startup. In addition, a title can load a Lingo Xtra while Director is running using openXlib, and subsequently close it using closeXlib. Generally, registration and operation of the Xtra are the same whether it was loaded from the Xtras folder at startup or explicitly loaded with openXlib.
- In Director 5.0, the IMoaMmInterrogator interface is ignored when opening a Lingo Xtra withopenXlib.
- If you call global or parent message handlers in a Lingo Xtra and then finish using the Xtra, using openXlib andcloseXlib will allow Director to unload the Xtra, potentially freeing some memory.
- If an Xtra file includes other Xtras than Lingo Xtras (such as a Tool Xtra that adds names to the Xtras menu), they will not be registered.
In the examples, particularly the Lingo examples, it looks as if they are calling functions instead of accessing via interfaces. For example, Draccess\Draccess.c just callsMmList_GetValueByIndex() without, apparently, any reference to the IMoaMmList interface. The Common glue code in XDK\Examples\Common\projsuprt.c,h generates this.
The macro MmList_GetValueByIndex() is inmmsuprt.h.
The call to QueryInterface to get theIMoaMmList interface is inMmSupport_Open(), also in mmsuprt.h.
There is similar support code for Moa and Director utilities.
The macro and function rely on there being a well-known structure in the Xtra's instance data. If you want to use these macros in your Xtra, you must do four things:
- You must declare the right structure in the EXTERN_BEGIN_DEFINE_CLASS_INSTANCE_VARS macro, which is usually inclassdef.h. The names are:
/* Used by support code */ MoaSupData moaSupData; MoaMmSupData moaMmSupData; MoaDrSupData moaDrSupData;
- You must call the glue routine that acquires the interfaces (MoaSupport_Open(), MmSupport_Open(), orDrSupport_Open()), usually in your Xtra's Create handler.
- You must call the glue routine that releases the interfaces (MoaSupport_Close(), MmSupport_Close(), or DrSupport_Close()), usually in your Xtra's Destroy handler.
- In exactly one C file you must #defineINIT_XXXSUPPORT, where XXX isDR, MM, or MOA, before including the XXXSUPPORT.H header file. This causes the actual code for the XxxSupportOpen() function to be incorporated.
See the Lingo examples, such asEXAMPLES\LINGO\DrAccess\Source\DrAccess.c to see how they do this.
Note: The Lingo Template example (LGOTMPLT) does not do all these steps. If you want to use the COMMON support macros, start from one of the other Lingo examples, such as JTLXtra or DrAccess.
COMMON\moasupprt.h uses QueryInterface() to get theIMoaCalloc interface, and stores a pointer to this in a moaMmSupData.pMoaCalloc field. But from Access to callback interfaces from an Xtra and its figure in MOA Concepts and Reference, pCalloc is a standard callback interface provided along with the pCallback gateway to all the other interfaces. So your object will already have this interface in its instance data. There's no need to do QueryInterface to get it, or Release it, or anything; it's just there inThis->pObj->pCalloc (in C) along with the rest of your instance data inThis->pObj->whatever.
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 -- which is true of almost all of the shipping samples -- 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.
When you create an Xtra in addition to making sure that all of your GUIDs are unique, you want to ensure that other items for your Xtra are unique.
If you create an asset Xtra (sprite or transition), your asset's type symbol (kMoaMmDictKey_SymbolString) must be unique. This is a single string, with a maximum of 32 characters (including a trailing null), that's typically defined in your implementation file and registered in yourIMoaRegister::Register() implementation.
Note: This is separate from your Xtra's display name (kMoaMmDictType_DisplayNameString) and display category (kMoaMmDictType_DisplayCategoryString), which are what the user sees in the host application's user interface. The type symbol is used internally to differentiate your Xtra from others, and is used when referring to your asset from Lingo.
If you create a Lingo Xtra, your Lingo Xtra's name (the name after xtra in the msgTable) must not conflict with other Lingo reserved words and other Xtras. Also, any global handlers (names that appear with a * before them in themsgTable) must likewise not conflict with other Lingo words.
As recommended in "Implementing an asset Xtra" in Director 5.0/Authorware 3.5 XDK Developer's Guide, it's a good idea to use a short, unique prefix for your organization, which can be the same for all the Xtras you implement.
Macromedia will register these symbols and keywords. Macromedia will not register GUIDs.
(from Christian Void) Here is one way to handle errors in a Director-only Xtra:
Trap the error internally and use CallHandler() to execute the Lingo alert() function to display your own error dialog.