ExternalInterface

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:

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

And register the handler with GFx::Loader:

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

An external interface call will be made from ActionScript when the text input box gains or lose focus. Open d3d9HUD.as used by d3d9guideAS3.fla and look at the ActionScript for the following functions:

    function onSubmit(path:String) {
        ExternalInterface.call("MeshPath", path);
        CloseMeshPath();
    }
        
    function onFocusIn(ev: FocusHandlerEvent) {
        ExternalInterface.call("MeshPathFocus", true);  
    }

    function onFocusOut(ev: FocusHandlerEvent) {
        ExternalInterface.call("MeshPathFocus", false); 
    }

Note:: The MeshPathFocus callback is called whenever the text input box gains or lose focus

The ExternalInterface calls will trigger our ExternalInterface handler and pass it the focus state of the text input box (true or false) and the arbitrary command string “MeshPathFocus.” Open the text input, click on the text area, and then click on an empty area of the screen to move focus away from the text input. The console output should be similar to:

Callback! MeshPathFocus, nargs = 1
  arg(0) = true

Callback! MeshPathFocus, nargs = 1
 arg(0) = false 

The event handler can be modified to detect when focus is gained or lost and pass that information to the GFxTutorial object:

if(strcmp(methodName, "MeshPathFocus") == 0 && argCount == 1 && 
  args[0].GetType() == Value::VT_Boolean)   {
    if (args[0].GetType() == Value::VT_Boolean)
        gfx->SetTextboxFocus(args[0].GetBool());
} 

GFxTutorial::ProcessEvent will only pass keyboard events to the movie if the textbox has focus. If a keyboard event is passed to the textbox, a flag is set to prevent it from being passed to the 3D engine:

    if (uMsg == WM_SYSKEYDOWN || uMsg == WM_SYSKEYUP || 
        uMsg == WM_KEYDOWN    || uMsg == WM_KEYUP    || 
        uMsg == WM_CHAR)
    {
        if (textboxHasFocus || wParam == 32 || wParam == 9)
        {
            ProcessKeyEvent(pUIMovie, uMsg, wParam, lParam);
            *pbNoFurtherProcessing = true;
        }
    }

Space (ASCII code 32) and tab (ASCII code 9) are always passed through as they correspond to the “Toggle UI” and “Settings” buttons.

In order to enable the user to change the mesh being rendered, they click the “Change Mesh” button to open the text box, enter a new mesh name, and then press enter. When enter is pressed, the text box will invoke an ActionScript event handler, which calls ExternalInterface with the name of the new mesh. The additional code in OurExternalInterfaceHandler::Callback is:

    static bool doChangeMesh = false;
    static wchar_t changeMeshFilename[MAX_PATH] = L"";

    ...

    if(strcmp(methodName, "MeshPath") == 0 && argCount == 1)
    {
        doChangeMesh = true;
        const char *filename = args[0].GetString();
        MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, filename, -1, 
            changeMeshFilename, _countof(changeMeshFilename));
    }

As with the fullscreen toggle, the actual work is done in the DXUT OnFrameMove callback. The code is based on the event handler for the default DXUT interface.