Article

A Little Something Xtra

A Little Something Xtra

Macromedia Director has proven itself in myriad applications as the development platform of choice. Although Director lets nonprogrammers create multimedia applications, there are a surprising number of developers with traditional software engineering backgrounds who use Director.

Why would software engineers, who are perfectly capable of writing in "professional" languages like C/C++, choose to develop applications in Lingo and Director instead of a "real" programming environment? Because Director, despite the occasional quirks, offers a significant advantage over more traditional languages.

I know this is why I originally chose Director as a development platform. After years of developing multimedia in C for DOS and Windows, I needed to find a way to create a product on the Macintosh. I was not in a position at that time to go up the Mac learning curve, so I started casting about for a cross-platform multimedia engine. And what I found was Macromedia Director v4.0, then new for Windows.

What intrigued me was not so much that Director could be used to develop cross-platfrom products - there were other programs that could claim as much - but that it had a powerful programming language, Lingo, and that it was extensible beyond that. Yes, extensible - if there were some feature I needed that Director didn't have, I could write that portion myself in C/C++, but I didn't necessarily have to write the whole application in C/C++. That was tremendously attractive.

Back then, extensions were done with XObjects, the precursor to Xtras. XObjects were much more limited than the current Xtras - you could write Lingo to call an XObject that could talk to the operating system and control attached devices or whatever you needed, and it could return information back to Director, but XObjects couldn't control things within Director itself (for instance, sprites, casts, etc.). It was a one-way street.

To make matters worse, the Windows and Macintosh SDKs for XObjects bore little relation to one another; many things were said about the Macintosh side in particular, few of them charitable.

All this changed with the introduction of Xtras with Director 5. Xtras in Director 5 was part of an effort to have all (or as many as possible) Macromedia products implement a common API called Macromedia Open Architecture, more simply known as MOA. One of the thoughts was that if you learned MOA, you could write plug-ins for multiple products. This would make things simpler for third parties, and presumably for Macromedia itself. One plug-in, if it didn't use product-specific functions, could work equally well in Authorware as Director. Another objective was to migrate certain key functions out of the runtime engine and into Xtras, so if an update was needed, just one Xtra needed to be updated and tested, but not the whole product.

And, in fact, this has all come true, with the possible exception of all products having been migrated over to MOA. Director supports MOA, as does Authorware, and certain of the server products.

What has also evolved over the years has been MOA itself. Originally an awkward collection of macros and involved COM-like C statements, it has slimmed down, is C++ friendly, and is much easier to start working with than it was originally. It has matured a lot, and nowadays it's comparatively easy to get up and running with the XDK (Xtra Development Kit).

In this article we'll look at just what is available via MOA and the XDK, and how you can put it to use. We won't actually try to write an Xtra, but I'll point you in the right direction in case you want to learn more, or take a crack at it yourself.

Types of Xtras
Macromedia Director recognizes four common types of Xtras. Scripting (or Lingo) Xtras let you develop your own Lingo commands, or make calls into the Xtra from Lingo. Sprite Xtras let you create a custom sprite type (for instance, a video sprite). Transition Xtras let you create your own custom transitions. Finally, Tool Xtras let you create tools that are available at authoring time (but not at runtime).

In truth there are actually five component types. The Sprite Xtra that I mentioned is really composed of two components - an asset component (this is the part that lives in the cast), and a Sprite Actor component (this is the part that exists on stage). Likewise, the Transition Xtra is really composed of an asset part and a transition actor part. You can, of course, use the actor and asset parts separately - someone might want to make a custom asset component to go with their scripting Xtra, say, but this is not common, although it is certainly allowable. It is far more common to find asset and actor components used in tandem to create Sprite Xtras or Transition Xtras.

Regrettably, the Director UI is misleading, as there is no one place to easily see what third-party Xtras are installed. They're scattered around the interface.

The understandably natural place to look first would be under the Xtras menu on the toolbar. This menu, in fact, lists only Tool Xtras, and Tool Xtras aren't the most common type of Xtra by a long shot. If you look there to see if your Xtra is installed, you'll probably be disappointed.

Where to look, then? Well, as mentioned, they're scattered about. You won't find transition Xtras until you go to insert a transition. Double-click on the transition frame and in the selection box you will find all the available transitions, including your third-party custom ones.

For Sprite Xtras, you have to go to the Insert menu, and there at the bottom you will find various types of Sprite Xtras that you can insert into the cast (technically you're creating an asset component when you do this). Once it's dragged onto the stage, that's the actor component. As mentioned, it's possible to create an asset without an actors should someone find some application where that would make sense.

Finally, Scripting, or Lingo, Xtras in Director MX you can now be seen them by clicking on the fifth Lingo button on the message window (or in the scripting window); the tooltip for this one says "Scripting Xtras" and if you click on it you will see a list of all available scripting Xtras, and their published commands. At the end of their command list is an entry that says "put interface". If you choose this, the command interface for the Xtra will be dumped to the message window.

Alternatively, from the message window, you can give the command "showxlib", which will list loaded Scripting Xtras. This is the old-fashioned (pre-MX) way. You can also show the interface for a scripting Xtra with the put interface command like so:

put interface(xtra "fileio")

Versions of Director prior to MX did not have the Scripting Xtras Lingo button, so users of Director 8.5 and earlier have to use this method to see scripting Xtras and their interfaces.

Writing Your Own Xtras
We don't have the space in this article for a how-to on writing your own Xtra, but I'll try to take some of the mystery out of it if you want to get started. We'll primarily look at Scripting and Sprite Xtras, with a quick glance at Tool and Transition Xtras.

First, you should at least know (or be willing to learn) C/C++ because Xtras are all done in C/C++. A few brave souls have tried to write Xtras in Delphi and Visual Basic, but it's not trivial to adapt the XDK for environments outside of the standard (the standard, by the way, is Microsoft Visual Studio on Windows, and CodeWarrior on Macintosh).

Second, you will need to have one of the aforementioned development environments, and the XDK from Macromedia. The XDK is free. To find it, go to the Macromedia site and type XDK in the search box. One of the top entries will be for the XDK for Director. The XDK typically lags behind versions of Director. As of this writing, Director MX is the most current release of Macromedia Director, but the most current XDK is for Macromedia Director 8.5.

The XDK comes with an Examples folder that has examples of the various types of Xtras, but most important also has folders in each named "skeleton", which is really a template project that you can use for your own Xtras.

Xtras use GUIDs to identify themselves, and often each class (the registration class, the implemented class, etc.) will have their own GUID, so you will need to use the GUIDGEN.EXE tool that comes with Visual Studio to make these unique numbers. The original skeleton files had at least two GUIDs and multiple files (one for registration, one for the main class, etc.). The XDK for Director 8.5 has a "skeleton2" folder that is a new slimmed-down version of the skeleton, reworked to condense the Xtra down to just one .CPP/.H file pair, and just one GUID (for scripting). If you are a new Xtras developer, you might want to try the newer Skeleton2 template - it might be easier.

Xtras are really .DLLs on Windows, and Code Fragments on the Macintosh. They have a registration class, which Director will call upon starting up, and the registration class tells Director what the Xtra is capable of and what services it needs and offers. Beyond that, you can stuff whatever sort of class you want into an Xtra. Each type has its own specific calls that it should expect to receive.

Scripting Xtras
Scripting Xtras, originally called Lingo Xtras, are invoked only via Lingo commands. You can use them to make global, parent, or child functions available in Lingo.

A Global Lingo command (not to be confused with global Lingo variables) is one that is available to all users that have the Xtra installed, and is issued like other Lingo commands. If you made a global Lingo command called FixAllBugs, then you could issue it from the message window or in a handler just by giving the command FixAllBugs(). Each scripting Xtra provides Director with a list of commands it supports when it registers with Director. The command table (called the message table) has a special character in front of each command to indicate whether it is global, parent, or child. An asterisk in front indicates the command is global. Use global commands sparingly - there is a limited namespace - only one Xtra can have any given name of a command. Global commands are often preferred if the target user is generally not familiar with programming or instantiation of objects. In such cases, giving the command a unique name, possibly preceded by the initials of your name, your company name, or your Xtra. An example might be cfxLoadImage, as opposed to a more generic LoadImage name.

Lest you think it might be a good thing to lock up the market on generic command names, I would caution you that internal Lingo names take precedence. Were you to name your command the more generic "loadImage", and a later release of Director Macromedia implemented a new command called "loadImage", then yours would no longer work, and all programs written with your Xtra using that function would cease to work. I ran into this in the CastEffects Xtra we used to sell (it's now off the market). That Xtra had functions called GetPixel and SetPixel. The introduction of Imaging Lingo in Director 7 also saw the introduction of two Lingo commands, GetPixel and SetPixel. Suddenly the CastEffects commands were unreachable, and users saw errors on their calls. The commands were reimplemented with prefixes, as in cfxGetPixel and cfxSetPixel, which makes it much less likely that there will be a name conflict in the future.

Child functions have to be instantiated, but they allow the Xtra to keep local data per instance around. An example of instantiating an Xtra is:

global infileObj
infileObj =new(xtra "fileio")

This makes an instance of this Xtra, kept in the global variable "infileObj". Internally it has its own set of class variables. You could, from the same Xtra, make another instance, thus keeping two files open at the same time. Internally, the FileIO Xtra (which comes with Director) keeps track of each file separately via independent class variables.

Scripting Xtras have a simple interface, ::Call. When a user makes a function call that matches the message table your Xtra provided at registration, Director will call the Xtra and pass in a call pointer to transfer some parameters. One parameter, MethodSelector, is an index to the table so you know what command was requested (developers use an intermediate enum table to make this easier, as shown in the template and examples). Other parameters are an argument count and the arguments to the call themselves. Finally, there is a result value that is how you pass back whatever value you want to return to Lingo.

Sprite Xtras
Sprite Xtras are, as mentioned, really composed of two components - the asset component and the actor component. The asset component is that part that exists when placed in a castmember. At registration, the asset component tells Director what capabilities it has. Examples of capabilities it might declare are that it can animate the image (thumb) that you see when it's in the cast window, that it has a custom properties editor and/or media editor, that it provides its own about box, etc.

Unlike the simple scripting Xtra, the asset component of a Sprite Xtra has many entry points where MOA can call it. These include GetProp and SetProp so you can get (or set) a property on the castmember, for instance, perhaps the width of your asset. Other entry points include opportunities to stream media out or in, such as during save movie time, so your asset can save out information and load it back in the next time. This is really a two-part process, starting with Director asking how much information, if any, you need to save out, and then a second call asking you to actually write out the information.

There are calls to image or animate your thumbnail, to invoke your property editor or media editor, and so forth. By responding to these, and other, calls, you are creating the ties that integrate your Asset Xtra into Director. Finally, there are calls so you know when the Xtra has been dragged to the stage. It is through these that you would normally want to create a new actor instance, thus creating a sprite from your asset Xtra.

The SpriteActor class also has GetProp and SetProp (so you can, from Lingo, get a custom property from the sprite) but also functions specific to its life as a sprite, such as imaging and event handling.

There are really only two imaging callbacks. One is ::CollectChanges(), which is called to see if you have anything different to draw than what you've drawn already. A sprite with a static image might report no changes. A sprite with a dynamic image, such as video, might always report something changed. What you are reporting back is what rectangle in your draw area has changed, if any, and needs to be redrawn.

The other imaging callback is ::Image(), which is where you do your actual drawing. Director calls this when it needs your Xtra to render its image, which will be done to Director's internal compositing buffer, or Direct to screen, depending on how you declared you were going to handle your rendering. You get a pointer to the existing offscreen buffer and you then have an opportunity to modify your portion of it, thus drawing the image of your sprite onto the buffer.

The event handling callbacks include a very important one, ::GetCaps(). It is in the GetCaps callback that your sprite actor Xtra declares what it is capable of and/or what it wants. For instance, it can indicate that it wants to receive mouse events. Or if it has an unchanging, static image all the time. Or whether it prefers to draw directly to screen or offscreen.

Other callbacks are ::Event() which would receive any events it asked to be notified of (like keyboard or mouse events), and ::ContainsPoint(), which is where it can indicate if it contains the specified point.

This last one may seem a little unusual - you might think that Director would know if a point fell within a sprite's rectangle - but in reality that's true only for rectangular sprites. If your sprite imaged itself as anything other than a rectangle, then only your sprite knows whether or not a point would fall within the "live" portion of the sprite. The point test is for various things, including clicks and collision testing.

Transition Xtras
Transition Xtras are similar in structure to Sprite Xtras. They also have an asset component, and they have a transition actor component. We covered the asset component in the Sprite Xtras discussion; as far as actors go, the transition actor component is similar.

Transition actors are much simpler, though, because they're not interactive. A transition is a custom way to visually transition from one screen image to another. This is not an instant change, but usually some special effect. A simple example would be a crossfade. An involved example might be a burning page effect. Regardless, they all involve a starting point (the original image), an ending point (the destination image), and a series of steps to get from one to the other over a specific period of time.

As such, the transition actor has three main callbacks, ::Cue, ::Continue, and ::Finish.

Cue is used to tell the actor to get ready for the transition. You will get pointers to the starting and ending images, a rectangle indicating the portion to be transitioned, and a small info table providing information such as how long the transition is to take, in what size chunks, and so forth.

Continue is called repeatedly during the transition period, with each call affording you the opportunity to render the next step in your transition.

Finish is called when Director wants you to draw the final image, whatever it might be, and allow the Xtra to clean up after itself. Finish might be called even if you haven't done all the steps you intended - perhaps the time period expired or for some other reason the transition period is over. At any rate, the transition is over and that's that. Finish up.

Tool Xtras
A Tool Xtra is intended for use only at authoring time, not runtime. What your tool does is up to you - perhaps it organizes the score for you, or analyzes cast members, or converts them, or exports them to another format. A Tool Xtra has a simple interface, consisting of the calls ::GetEnabled() and ::Invoke().

GetEnabled is called when the user clicks on the Xtras menu on the toolbar. Director has to determine whether or not the menu item for your Tool Xtra is enabled or not. You might want some tools to always be enabled. You might enable others only if there is a selection made in the cast window, for instance. What you return here determines whether or not your tool will appear grayed out or active.

Invoke, as the name implies, means that the user has selected the Xtra from the Xtras menu, thus invoking it. Now your tool can go to work.

What Xtras Can Access
Because Xtras are typically written in C/C++, they can access all functions in the operating system. They can create threads, write files, trap messages, and so forth. But they also have access to various internal Macromedia functions. This is MOA, the public internals of Director.

Most of MOA is given in a COM-like format, with a long list of classes. We can't get into them in depth here, but as a quick overview:

  • IMoaMmUtils2: Provides helpful utilities, mainly the ability to print messages in the message window.
  • IMoaMmValue: Provides value conversions. Since Lingo is a loosely typed language - a variable can hold an integer one moment, a string the next - all data is passed around in MoaMmValue format, which is really a two-part structure - one part indicates what the value is (e.g., a string), and the other part is a data pointer to the information itself. IMoaMmValue provides the conversion functions to get information in and out of MoaMmValue format.
  • IMoaMmList: Provides functionality to create and access Lingo lists (property lists and linear lists).
  • IMoaDrPlayer: Provides access to the current player (the runtime engine). From here you can get a pointer to the current active movie (IMoaDrMovie), and then to the score (IMoaDrScore Access), the cast, and so on, or you can use it to call a handler in the current movie, allowing you to call Lingo from within the Xtra.
There are many, many more interfaces and classes. There are some for managing memory, for managing files, for streaming, for creating image objects, for traversing score frames, and so on. You can't get at absolutely everything in Director, but you can get at a surprising amount.

Where to Go From Here
If you are seriously interested in writing Xtras, you'll want to join the e-mail listserv mmxdk-l hosted by Macromedia. At the time of this writing they had just switched listservers, and there was no public sign-up form for the new server yet. I would suggest just going to their site and searching on mmxdk; if that doesn't help, e-mail me at [email protected] and I will see what I can find out.

Certainly you should get the XDK and read through the docs. Try compiling sample Xtras. You can also buy many off-the-shelf Xtras - check out Macromedia's Director Exchange, and you can have custom Xtras made as well if you're not in a position, timewise or otherwise, to write your own.

The important thing to take away from all this is that Director is not a self-contained, static entity. You can extend it far and wide. For many years we've written custom Xtras for all sorts of applications - everything from controlling ZModem file transfers to controlling DV camcorders over FireWire, from exporting animations to proprietary formats, to music, to interfacing with Lotus Notes. There are few limitations, and since Shockwave runs in browsers, you effectively give yourself access to all this functionality in a browser environment as well as a desktop app. I can think of no other tool on the market that gives cross-platform rapid application development, in both browser and desktop, and still lets me extend the product as necessary to meet my needs. That's why I continue to use Macromedia Director to develop multimedia software products.

More Stories By Tab Julius

Tab Julius has been writing software since the mid-70s, and now works for a software firm developing medical imaging applications, although he still does limited consulting on the side.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.