ActionScript to C++

Scaleform provides two mechanisms for the C++ application to receive events from ActionScript: FSCommand and ExternalInterface. These mechanisms are part of the standard ActionScript API and more information on them can be found in the Flash documentation. Both FSCommand and ExternalInterface register a C++ event handler with Scaleform to receive event notification. These handlers are registered as Scaleform states and therefore can be registered on a GFx::Loader, GFx::MovieDef or GFx::Movie depending on functional requirements. For information about GFx states, please refer to the Scaleform documentation.

FSCommand events are triggered by the ActionScript 'fscommand' function. The fscommand function can only pass two string arguments from ActionScript, one for a command and one for a single data argument. These string values are received by the C++ handler as two const char pointers. The C++ fscommand handler unfortunately cannot return a value back as the ActionScript fscommand function has no return value support.

ExternalInterface events are triggered when ActionScript calls the flash.external.ExternalInterface.call function. This interface can pass an arbitrary list of ActionScript values in addition to a command name. The C++ ExternalInterface handler receives a const char pointer for the command name and an array of GFx::Value arguments corresponding to the ActionScript values passed from the runtime. The C++ ExternalInterface handler can also return a GFx::Value back to the runtime as the ActionScript ExternalInterface.call method supports a return value. The ActionScript ExternalInterface class is part of the External API, an application programming interface that enables straightforward communication between ActionScript and the Flash Player container, in this case the application using Scaleform.

Due to limited flexibility, FSCommands are made obsolete by ExternalInterface and are no longer recommended for use. They are described here for completeness and legacy support.

To an extent, both FSCommands and ExternalInterface are made obsolete by the Direct Access API and the GFx::FunctionHandler interface. This interface allows direct callbacks to C++ methods be assigned inside the ActionScript VM as normal functions. When the function is invoked in ActionScript, it will in turn invoke the C++ callback. For more information on GFx::FunctionHandler, please see the Direct Access API section.

FSCommand Callbacks

The ActionScript fscommand function passes a command and a data argument to the host application. The following is a typical usage in ActionScript:

fscommand("setMode", "2");

Any non-string arguments to fscommand, such as boolean or integers will be converted to strings.

An ActionScript fscommand call passes two strings to the GFx FSCommand handler. An application registers a fscommand handler by sub-classing GFx::FSCommandHandler and registering an instance of the class as a shared state with either the GFx::Loader, GFx::MovieDef or with individual GFx::Movie objects. Keep in mind that the settings between state bags are intended to be delegated as follows: GFx::Loader -> GFx::MovieDef -> GFx::Movie. They can be overridden in any one of those later instances. This means that if you want a particular GFx::Movie to have its own GFx::RenderConfig (for separate EdgeAA control, for example) or GFx::Translator (so that it is translated to a different language), you can set it on that object and whatever states you apply there will take precedence over states applied on the object higher in the delegation chain, such as GFx::Loader. If a command handler is set on a GFx::Movie, it will receive callbacks for only the FSCommand calls invoked in that movie instance. The GFxPlayerTiny example demonstrates this process (search for “FxPlayerFSCommandHandler”).

The following is an example of fscommand handler setup. The handler is derived from GFx::FSCommandHandler.

class OurFSCommandHandler : public GFx::FSCommandHandler
{
    public:
virtual void Callback(Movie* pmovie, const char* pcommand,
                      const char* parg)
    {
        printf("FSCommand: %s, Args: %s", pcommand, parg);
    }
};

The Callback method receives the two string arguments passed to fscommand in ActionScript as well as a pointer to the specific movie instance that invoked fscommand. Our custom handler simply prints each fscommand event to the debug console.

Next, register the handler after creating the GFx::Loader object:

// Register our fscommand handler
Ptr<FSCommandHandler> pcommandHandler = *new OurFSCommandHandler;
gfxLoader->SetFSCommandHandler(pcommandHandler);

Registering the handler with the GFx::Loader causes every GFx::Movie and GFx::MovieDef to inherit this handler. SetFSCommandHandler can be called on individual movie instances to override this default setting.

In general, C++ event handlers should be non-blocking and return to the caller as soon as possible. Event handlers are typically only called during Advance or Invoke calls.

The External Interface API

The Flash ExternalInterface.call method is similar to fscommand but is preferred because it provides more flexible argument handling and can return values. Registering an ExternalInterface handler is similar to registering an fscommand handler. Here is an example ExternalInterface handler that prints out number and string arguments.

class OurExternalInterfaceHandler : public ExternalInterface
{
    public:
virtual void Callback(Movie* pmovieView, const char* methodName,
                                  const Value* args, unsigned argCount)
    {
          printf("ExternalInterface: %s, %d args: ", methodName, argCount);
        for(unsigned i = 0; i < argCount; i++)
        {
            switch(args[i].GetType())
            {
                case Value::VT_Number:
                printf("%3.3f", args[i].GetNumber());
                break;
                case Value::VT_String:
                printf("%s", args[i].GetString());
                break;
            }
            printf("%s", (i == argCount - 1) ? "" : ", ");
        }
        printf("\n");

        // return a value of 100
        Value retValue;
  retValue.SetNumber(100);
        pmovieView->SetExternalInterfaceRetVal(retValue);
      }   
};

Once the handler is implemented, an instance of it can then be registered with GFx::Loader as shown below:

Ptr<ExternalInterface> pEIHandler = *new OurExternalInterfaceHandler;
gfxLoader.SetExternalInterface(pEIHandler);

Now the callback handler will be triggered by an ExternalInterface call in ActionScript.

FxDelegate

The ExternalInterface handler above provides very basic callback functionality. In practice, an application might like to register specific functions to be called whenever an ExternalInterface callback is triggered with a certain methodName. This requires a more advanced callback system which allows functions to be registered with a corresponding methodName, and then uses a hash table (or similar) to look up and execute them with the corresponding GFx::Value arguments. Such a system is provided in our samples and is called FxDelegate (see Apps\Samples\GameDelegate\FxGameDelegate.h).

FxDelegate derives from GFx::ExternalInterface, as expected. It has functions Register/Unregister Handlers which allows an app to install its own callback handlers as delegates, registered by a methodName.

For an example of FxDelegate usage, please take a look at our HUD Kit which utilizes FxDelegate for its minimap implementation. Here is the application code from the minimap demo which registers it’s callback functions with FxDelegate:

// *** Minimap Begin
void FxPlayerApp::Accept(FxDelegateHandler::CallbackProcessor* cbreg)
{
    cbreg->Process("registerMiniMapView", FxPlayerApp::RegisterMiniMapView);

    cbreg->Process("enableSimulation", FxPlayerApp::EnableSimulation);
    cbreg->Process("changeMode", FxPlayerApp::ChangeMode);
    cbreg->Process("captureStats", FxPlayerApp::CaptureStats);

    cbreg->Process("loadSettings", FxPlayerApp::LoadSettings);
    cbreg->Process("numFriendliesChange", FxPlayerApp::NumFriendliesChange);
    cbreg->Process("numEnemiesChange", FxPlayerApp::NumEnemiesChange);
    cbreg->Process("numFlagsChange", FxPlayerApp::NumFlagsChange);
    cbreg->Process("numObjectivesChange", FxPlayerApp::NumObjectivesChange);
    cbreg->Process("numWaypointsChange", FxPlayerApp::NumWaypointsChange);
    cbreg->Process("playerMovementChange", FxPlayerApp::PlayerMovementChange);
    cbreg->Process("botMovementChange", FxPlayerApp::BotMovementChange);

    cbreg->Process("showingUI", FxPlayerApp::ShowingUI);
}

FxDelegate is fully functional and is provided as an example of a more sophisticated callback handling and registration system. It is meant to be a starting point for the user and we encourage developers to look at the details of how it works and customize and extend it for their own needs.

Using External Interface from ActionScript

In ActionScript, an External Interface call would be initiated with the following code:

import flash.external.*;
ExternalInterface.call("foo", arg);

where ‘foo’ is the method name and ‘arg’ is an argument of basic type. You can specify 0 or more parameters, separated by commas. Please refer to the Flash documentation for more information about the ExternalInterface API.

If ExternalInterface has not been imported as in the code above, the ExternalInterface calls must be fully qualified:

flash.external.ExternalInterface.call("foo", arg)

ExternalInterface.addCallback

The ExternalInterface.addCallback function globally registers an ActionScript method under an alias. This allows registered methods to be invoked from C++ without a path prefix. It supports registering the method and the calling context ("this" objects) under a single alias. The registered alias can be called by the C++ GFx::Movie::Invoke() method.

Example:

AS Code:
import flash.external.*;

var txtField:TextField = this.createTextField("txtField",
                                    this.getNextHighestDepth(), 0, 0, 200, 50);

varaliasName:String = "setText";
var instance:Object = txtField;
var method:Function = SetText;
var wasSuccessful:Boolean = ExternalInterface.addCallback(aliasName, instance,
                                                          method);
function SetText()
{
    trace(this + ".SetText ");
    this.text = "INVOKED!";
}

Now it is possible to call the SetText ActionScript function with "this" set to txtField by invoking the alias:

C++ Code:
pMovie->Invoke("setText", "");

The result will trace "txtField.SetText" and set the text "INVOKED!" to the "txtField" text field. See section Executing ActionScript Subroutines in C++ to ActionScript for more information on using GFx::Movie::Invoke().