From 59210bf71831ef777589fe85ab3e9a73fb3b90e6 Mon Sep 17 00:00:00 2001 From: David Rose Date: Tue, 7 Jul 2009 01:05:41 +0000 Subject: [PATCH] more work on outbound scripting --- direct/src/plugin/p3dInstance.cxx | 10 +- direct/src/plugin/p3dInstance.h | 2 +- direct/src/plugin/p3dPythonObject.cxx | 6 +- direct/src/plugin/p3dPythonRun.cxx | 154 +++++++++++++++----- direct/src/plugin/p3dPythonRun.h | 5 +- direct/src/plugin/p3dSession.cxx | 27 +++- direct/src/plugin/p3dSession.h | 4 +- direct/src/plugin_npapi/ppBrowserObject.cxx | 2 +- direct/src/plugin_npapi/ppInstance.cxx | 42 +----- direct/src/plugin_npapi/ppInstance.h | 5 +- direct/src/plugin_npapi/ppPandaObject.cxx | 4 +- direct/src/showutil/runp3d.py | 64 ++++++-- 12 files changed, 214 insertions(+), 111 deletions(-) diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index 08d6f50a9e..94b28d7182 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/direct/src/plugin/p3dInstance.cxx @@ -173,7 +173,7 @@ set_wparams(const P3DWindowParams &wparams) { // of the instance, to be used by JavaScript code in the // browser to control this program. //////////////////////////////////////////////////////////////////// -P3DObject *P3DInstance:: +P3D_object *P3DInstance:: get_panda_script_object() const { assert(_session != NULL); nout << "Called P3DInstance::get_panda_script_object()\n"; @@ -188,13 +188,13 @@ get_panda_script_object() const { TiXmlDocument *response = _session->command_and_response(doc); nout << "response pointer: " << response << "\n" << flush; - P3DObject *result = NULL; + P3D_object *result = NULL; if (response != NULL) { TiXmlElement *xresponse = response->FirstChildElement("response"); if (xresponse != NULL) { TiXmlElement *xvalue = xresponse->FirstChildElement("value"); if (xvalue != NULL) { - result = _session->xml_to_object(xvalue); + result = _session->xml_to_p3dobj(xvalue); } } delete response; @@ -464,7 +464,7 @@ send_browser_script_object() { xcommand->SetAttribute("cmd", "pyobj"); xcommand->SetAttribute("op", "set_browser_script_object"); if (_browser_script_object != NULL) { - xcommand->LinkEndChild(_session->object_to_xml(_browser_script_object)); + xcommand->LinkEndChild(_session->p3dobj_to_xml(_browser_script_object)); } doc->LinkEndChild(decl); @@ -534,7 +534,7 @@ handle_script_request(P3D_request *request) { doc->LinkEndChild(decl); doc->LinkEndChild(xcommand); if (value != NULL) { - xcommand->LinkEndChild(_session->object_to_xml(value)); + xcommand->LinkEndChild(_session->p3dobj_to_xml(value)); P3D_OBJECT_FINISH(value); } diff --git a/direct/src/plugin/p3dInstance.h b/direct/src/plugin/p3dInstance.h index 1799feb5a7..9c41b66ec2 100644 --- a/direct/src/plugin/p3dInstance.h +++ b/direct/src/plugin/p3dInstance.h @@ -46,7 +46,7 @@ public: void set_wparams(const P3DWindowParams &wparams); inline const P3DWindowParams &get_wparams() const; - P3DObject *get_panda_script_object() const; + P3D_object *get_panda_script_object() const; void set_browser_script_object(P3D_object *object); bool has_request(); diff --git a/direct/src/plugin/p3dPythonObject.cxx b/direct/src/plugin/p3dPythonObject.cxx index 07c8039f08..1ba0968244 100644 --- a/direct/src/plugin/p3dPythonObject.cxx +++ b/direct/src/plugin/p3dPythonObject.cxx @@ -194,7 +194,7 @@ call(const string &method_name, P3D_object *params[], int num_params) const { } for (int i = 0; i < num_params; ++i) { - TiXmlElement *xparams = _session->object_to_xml(params[i]); + TiXmlElement *xparams = _session->p3dobj_to_xml(params[i]); xcommand->LinkEndChild(xparams); // Now we're done with the params object passed in, we can delete @@ -207,13 +207,13 @@ call(const string &method_name, P3D_object *params[], int num_params) const { TiXmlDocument *response = _session->command_and_response(doc); nout << "call response pointer: " << response << "\n" << flush; - P3DObject *result = NULL; + P3D_object *result = NULL; if (response != NULL) { TiXmlElement *xresponse = response->FirstChildElement("response"); if (xresponse != NULL) { TiXmlElement *xvalue = xresponse->FirstChildElement("value"); if (xvalue != NULL) { - result = _session->xml_to_object(xvalue); + result = _session->xml_to_p3dobj(xvalue); } } delete response; diff --git a/direct/src/plugin/p3dPythonRun.cxx b/direct/src/plugin/p3dPythonRun.cxx index 7bff32a930..ffbcea91ab 100755 --- a/direct/src/plugin/p3dPythonRun.cxx +++ b/direct/src/plugin/p3dPythonRun.cxx @@ -172,7 +172,7 @@ run_python() { // Now add check_comm() as a task. - _check_comm_task = new GenericAsyncTask("check_comm", st_check_comm, this); + _check_comm_task = new GenericAsyncTask("check_comm", task_check_comm, this); AsyncTaskManager *task_mgr = AsyncTaskManager::get_global_ptr(); task_mgr->add(_check_comm_task); @@ -244,7 +244,7 @@ handle_command(TiXmlDocument *doc) { } else if (strcmp(cmd, "script_response") == 0) { // Response from a script request. assert(!needs_response); - handle_script_response_command(xcommand); + nout << "Ignoring unexpected script_response\n"; } else { nout << "Unhandled command " << cmd << "\n"; @@ -408,41 +408,16 @@ handle_pyobj_command(TiXmlElement *xcommand, bool needs_response, } } -//////////////////////////////////////////////////////////////////// -// Function: P3DPythonRun::handle_script_response_command -// Access: Private -// Description: Handles the script_response command, a response from -// the browser to a previous script request from this -// process. -//////////////////////////////////////////////////////////////////// -void P3DPythonRun:: -handle_script_response_command(TiXmlElement *xcommand) { - int unique_id; - if (xcommand->QueryIntAttribute("unique_id", &unique_id) == TIXML_SUCCESS) { - PyObject *value = NULL; - TiXmlElement *xvalue = xcommand->FirstChildElement("value"); - if (xvalue != NULL) { - value = xml_to_pyobj(xvalue); - } else { - value = Py_None; - Py_INCREF(value); - } - PyObject *result = PyObject_CallMethod - (_runner, (char *)"scriptResponse", (char *)"iO", unique_id, value); - Py_DECREF(value); - } -} - //////////////////////////////////////////////////////////////////// // Function: P3DPythonRun::check_comm // Access: Private -// Description: This method is added to the Python task manager (via -// py_check_comm, below) so that it gets a call every +// Description: This method is added to the task manager (via +// task_check_comm, below) so that it gets a call every // frame. Its job is to check for commands received // from the plugin host in the parent process. //////////////////////////////////////////////////////////////////// -AsyncTask::DoneStatus P3DPythonRun:: -check_comm(GenericAsyncTask *task) { +void P3DPythonRun:: +check_comm() { ACQUIRE_LOCK(_commands_lock); while (!_commands.empty()) { TiXmlDocument *doc = _commands.front(); @@ -461,21 +436,82 @@ check_comm(GenericAsyncTask *task) { } RELEASE_LOCK(_commands_lock); - - return AsyncTask::DS_cont; } //////////////////////////////////////////////////////////////////// -// Function: P3DPythonRun::st_check_comm +// Function: P3DPythonRun::task_check_comm // Access: Private, Static // Description: This static function wrapper around check_comm is // necessary to add the method function to the // GenericAsyncTask object. //////////////////////////////////////////////////////////////////// AsyncTask::DoneStatus P3DPythonRun:: -st_check_comm(GenericAsyncTask *task, void *user_data) { +task_check_comm(GenericAsyncTask *task, void *user_data) { P3DPythonRun *self = (P3DPythonRun *)user_data; - return self->check_comm(task); + self->check_comm(); + return AsyncTask::DS_cont; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DPythonRun::wait_script_response +// Access: Private +// Description: This method is similar to check_comm(), above, but +// instead of handling all events, it waits for a +// specific script_response ID to come back from the +// browser, and leaves all other events in the queue. +//////////////////////////////////////////////////////////////////// +TiXmlDocument *P3DPythonRun:: +wait_script_response(int response_id) { + while (true) { + ACQUIRE_LOCK(_commands_lock); + + Commands::iterator ci; + for (ci = _commands.begin(); ci != _commands.end(); ++ci) { + TiXmlDocument *doc = (*ci); + + TiXmlElement *xcommand = doc->FirstChildElement("command"); + if (xcommand != NULL) { + const char *cmd = xcommand->Attribute("cmd"); + if (cmd != NULL && strcmp(cmd, "script_response") == 0) { + int unique_id; + if (xcommand->QueryIntAttribute("unique_id", &unique_id) == TIXML_SUCCESS) { + if (unique_id == response_id) { + // This is the response we were waiting for. + _commands.erase(ci); + RELEASE_LOCK(_commands_lock); + return doc; + } + } + } + + // It's not the response we're waiting for, but maybe we need + // to handle it anyway. + bool needs_response = false; + int want_response_id; + if (xcommand->QueryIntAttribute("want_response_id", &want_response_id) == TIXML_SUCCESS) { + // This command will be wanting a response. We'd better + // honor it right away, or we risk deadlock with the browser + // process and the Python process waiting for each other. + _commands.erase(ci); + RELEASE_LOCK(_commands_lock); + handle_command(doc); + delete doc; + ACQUIRE_LOCK(_commands_lock); + break; + } + } + } + + if (!_program_continue) { + terminate_session(); + } + + RELEASE_LOCK(_commands_lock); + + // It hasn't shown up yet. Give the sub-thread a chance to + // process the input and append it to the queue. + Thread::force_yield(); + } } //////////////////////////////////////////////////////////////////// @@ -494,6 +530,38 @@ py_request_func(PyObject *args) { return NULL; } + if (strcmp(request_type, "wait_script_response") == 0) { + // This is a special case. Instead of generating a new request, + // this means to wait for a particular script_response to come in + // on the wire. + int response_id; + if (!PyArg_ParseTuple(extra_args, "i", &response_id)) { + Py_DECREF(extra_args); + return NULL; + } + + nout << "Waiting for script_response " << response_id << "\n"; + TiXmlDocument *doc = wait_script_response(response_id); + nout << "got: " << *doc << "\n"; + TiXmlElement *xcommand = doc->FirstChildElement("command"); + assert(xcommand != NULL); + TiXmlElement *xvalue = xcommand->FirstChildElement("value"); + + PyObject *value = NULL; + if (xvalue != NULL) { + nout << "Converting xvalue: " << *xvalue << "\n"; + value = xml_to_pyobj(xvalue); + } else { + value = Py_None; + Py_INCREF(value); + } + nout << "Got script_response " << response_id << ", xvalue = " << xvalue << "\n"; + + delete doc; + Py_DECREF(extra_args); + return value; + } + TiXmlDocument doc; TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", ""); TiXmlElement *xrequest = new TiXmlElement("request"); @@ -506,6 +574,7 @@ py_request_func(PyObject *args) { // A general notification to be sent directly to the instance. const char *message; if (!PyArg_ParseTuple(extra_args, "s", &message)) { + Py_DECREF(extra_args); return NULL; } @@ -522,8 +591,10 @@ py_request_func(PyObject *args) { int unique_id; if (!PyArg_ParseTuple(extra_args, "sOsOi", &operation, &object, &property_name, &value, &unique_id)) { + Py_DECREF(extra_args); return NULL; } + xrequest->SetAttribute("operation", operation); xrequest->SetAttribute("property_name", property_name); xrequest->SetAttribute("unique_id", unique_id); @@ -542,6 +613,7 @@ py_request_func(PyObject *args) { const char *expression; int unique_id; if (!PyArg_ParseTuple(extra_args, "si", &expression, &unique_id)) { + Py_DECREF(extra_args); return NULL; } @@ -554,9 +626,11 @@ py_request_func(PyObject *args) { } else { string message = string("Unsupported request type: ") + string(request_type); PyErr_SetString(PyExc_ValueError, message.c_str()); + Py_DECREF(extra_args); return NULL; } + Py_DECREF(extra_args); return Py_BuildValue(""); } @@ -780,6 +854,11 @@ terminate_session() { } Py_DECREF(result); nout << "done calling stop()\n"; + + // The task manager is cleaned up. Let's exit immediately here, + // rather than returning all the way up. This just makes it easier + // when we call terminate_session() from a deeply-nested loop. + exit(0); } //////////////////////////////////////////////////////////////////// @@ -921,7 +1000,8 @@ xml_to_pyobj(TiXmlElement *xvalue) { int object_id; if (xvalue->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) { // Construct a new BrowserObject wrapper around this object. - return PyObject_CallFunction(_browser_object_class, (char *)"i", object_id); + return PyObject_CallFunction(_browser_object_class, (char *)"Oi", + _runner, object_id); } } else if (strcmp(type, "python") == 0) { diff --git a/direct/src/plugin/p3dPythonRun.h b/direct/src/plugin/p3dPythonRun.h index 89596d08f6..6b925ed9af 100755 --- a/direct/src/plugin/p3dPythonRun.h +++ b/direct/src/plugin/p3dPythonRun.h @@ -74,8 +74,9 @@ private: int want_response_id); void handle_script_response_command(TiXmlElement *xcommand); - AsyncTask::DoneStatus check_comm(GenericAsyncTask *task); - static AsyncTask::DoneStatus st_check_comm(GenericAsyncTask *task, void *user_data); + void check_comm(); + static AsyncTask::DoneStatus task_check_comm(GenericAsyncTask *task, void *user_data); + TiXmlDocument *wait_script_response(int response_id); PyObject *py_request_func(PyObject *args); static PyObject *st_request_func(PyObject *, PyObject *args); diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx index afcda25621..8af2df5de7 100644 --- a/direct/src/plugin/p3dSession.cxx +++ b/direct/src/plugin/p3dSession.cxx @@ -312,14 +312,14 @@ command_and_response(TiXmlDocument *command) { } //////////////////////////////////////////////////////////////////// -// Function: P3DSession::xml_to_object +// Function: P3DSession::xml_to_p3dobj // Access: Public // Description: Converts the XML representation of the particular -// object value into a corresponding P3DObject object. +// object value into a corresponding P3D_object. // Returns the newly-allocated object. //////////////////////////////////////////////////////////////////// -P3DObject *P3DSession:: -xml_to_object(const TiXmlElement *xvalue) { +P3D_object *P3DSession:: +xml_to_p3dobj(const TiXmlElement *xvalue) { const char *type = xvalue->Attribute("type"); if (strcmp(type, "none") == 0) { return new P3DNoneObject; @@ -350,6 +350,15 @@ xml_to_object(const TiXmlElement *xvalue) { return new P3DStringObject(*value); } + } else if (strcmp(type, "browser") == 0) { + int object_id; + if (xvalue->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) { + P3D_object *obj = (P3D_object *)object_id; + nout << "Found object " << obj << "\n" << flush; + nout << " formatted is " << *obj << "\n" << flush; + return P3D_OBJECT_COPY(obj); + } + } else if (strcmp(type, "python") == 0) { int object_id; if (xvalue->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) { @@ -362,14 +371,14 @@ xml_to_object(const TiXmlElement *xvalue) { } //////////////////////////////////////////////////////////////////// -// Function: P3DSession::object_to_xml +// Function: P3DSession::p3dobj_to_xml // Access: Public // Description: Allocates and returns a new XML structure // corresponding to the indicated value. The supplied // P3DObject passed in is *not* deleted. //////////////////////////////////////////////////////////////////// TiXmlElement *P3DSession:: -object_to_xml(const P3D_object *obj) { +p3dobj_to_xml(const P3D_object *obj) { TiXmlElement *xvalue = new TiXmlElement("value"); switch (P3D_OBJECT_GET_TYPE(obj)) { @@ -681,7 +690,11 @@ rt_make_p3d_request(TiXmlElement *xrequest) { xrequest->Attribute("unique_id", &unique_id); if (operation != NULL && xobject != NULL) { - P3D_object *object = xml_to_object(xobject); + nout << "xobject = " << *xobject << "\n" << flush; + P3D_object *object = xml_to_p3dobj(xobject); + nout << "converted to " << object << "\n" << flush; + nout << "object = " << *object << "\n" << flush; + nout << "operation = " << *operation << "\n" << flush; if (strcmp(operation, "get_property") == 0 && property_name != NULL) { request = new P3D_request; request->_request_type = P3D_RT_script; diff --git a/direct/src/plugin/p3dSession.h b/direct/src/plugin/p3dSession.h index 4e93e04a17..a36b7fd31b 100644 --- a/direct/src/plugin/p3dSession.h +++ b/direct/src/plugin/p3dSession.h @@ -49,8 +49,8 @@ public: void send_command(TiXmlDocument *command); TiXmlDocument *command_and_response(TiXmlDocument *command); - P3DObject *xml_to_object(const TiXmlElement *xvalue); - TiXmlElement *object_to_xml(const P3D_object *obj); + P3D_object *xml_to_p3dobj(const TiXmlElement *xvalue); + TiXmlElement *p3dobj_to_xml(const P3D_object *obj); private: void install_progress(P3DPackage *package, double progress); diff --git a/direct/src/plugin_npapi/ppBrowserObject.cxx b/direct/src/plugin_npapi/ppBrowserObject.cxx index c7fd7396a7..5d72691010 100644 --- a/direct/src/plugin_npapi/ppBrowserObject.cxx +++ b/direct/src/plugin_npapi/ppBrowserObject.cxx @@ -128,7 +128,7 @@ get_property(const string &property) const { return NULL; } - P3D_object *object = _instance->variant_to_object(&result); + P3D_object *object = _instance->variant_to_p3dobj(&result); browser->releasevariantvalue(&result); return object; } diff --git a/direct/src/plugin_npapi/ppInstance.cxx b/direct/src/plugin_npapi/ppInstance.cxx index a713c134a8..575c51f738 100644 --- a/direct/src/plugin_npapi/ppInstance.cxx +++ b/direct/src/plugin_npapi/ppInstance.cxx @@ -499,13 +499,13 @@ get_panda_script_object() { } //////////////////////////////////////////////////////////////////// -// Function: PPInstance::object_to_variant +// Function: PPInstance::p3dobj_to_variant // Access: Private // Description: Converts the indicated P3D_object to the equivalent // NPVariant, and stores it in result. //////////////////////////////////////////////////////////////////// void PPInstance:: -object_to_variant(NPVariant *result, const P3D_object *object) { +p3dobj_to_variant(NPVariant *result, const P3D_object *object) { switch (P3D_OBJECT_GET_TYPE(object)) { case P3D_OT_none: VOID_TO_NPVARIANT(*result); @@ -542,7 +542,7 @@ object_to_variant(NPVariant *result, const P3D_object *object) { } //////////////////////////////////////////////////////////////////// -// Function: PPInstance::variant_to_object +// Function: PPInstance::variant_to_p3dobj // Access: Private // Description: Converts the indicated NPVariant to the equivalent // P3D_object, and returns it (newly-allocated). The @@ -550,7 +550,7 @@ object_to_variant(NPVariant *result, const P3D_object *object) { // later. //////////////////////////////////////////////////////////////////// P3D_object *PPInstance:: -variant_to_object(const NPVariant *variant) { +variant_to_p3dobj(const NPVariant *variant) { if (NPVARIANT_IS_VOID(*variant) || NPVARIANT_IS_NULL(*variant)) { return P3D_new_none_object(); @@ -564,8 +564,7 @@ variant_to_object(const NPVariant *variant) { NPString str = NPVARIANT_TO_STRING(*variant); return P3D_new_string_object(str.utf8characters, str.utf8length); } else if (NPVARIANT_IS_OBJECT(*variant)) { - // TODO. - return P3D_new_none_object(); + return new PPBrowserObject(this, NPVARIANT_TO_OBJECT(*variant)); } // Hmm, none of the above? @@ -826,37 +825,6 @@ show_np_variant(const NPVariant &result) { } } -//////////////////////////////////////////////////////////////////// -// Function: PPInstance::np_variant_to_object -// Access: Private -// Description: Returns a freshly-allocated P3D_object corresponding -// to the indicated NPVariant. -//////////////////////////////////////////////////////////////////// -P3D_object *PPInstance:: -np_variant_to_object(const NPVariant &result) { - if (NPVARIANT_IS_NULL(result)) { - return NULL; - } else if (NPVARIANT_IS_VOID(result)) { - return P3D_new_none_object(); - } else if (NPVARIANT_IS_BOOLEAN(result)) { - return P3D_new_bool_object(NPVARIANT_TO_BOOLEAN(result)); - } else if (NPVARIANT_IS_INT32(result)) { - return P3D_new_int_object(NPVARIANT_TO_INT32(result)); - } else if (NPVARIANT_IS_DOUBLE(result)) { - return P3D_new_float_object(NPVARIANT_TO_DOUBLE(result)); - } else if (NPVARIANT_IS_STRING(result)) { - NPString str = NPVARIANT_TO_STRING(result); - return P3D_new_string_object(str.utf8characters, str.utf8length); - } else if (NPVARIANT_IS_OBJECT(result)) { - // TODO? - return P3D_new_none_object(); - // NPVARIANT_TO_OBJECT(result); - } - - // Huh, what is this? - return NULL; -} - #ifdef _WIN32 //////////////////////////////////////////////////////////////////// diff --git a/direct/src/plugin_npapi/ppInstance.h b/direct/src/plugin_npapi/ppInstance.h index ff88e9fb9e..414beaed07 100644 --- a/direct/src/plugin_npapi/ppInstance.h +++ b/direct/src/plugin_npapi/ppInstance.h @@ -52,8 +52,8 @@ public: NPObject *get_panda_script_object(); - void object_to_variant(NPVariant *result, const P3D_object *object); - P3D_object *variant_to_object(const NPVariant *variant); + void p3dobj_to_variant(NPVariant *result, const P3D_object *object); + P3D_object *variant_to_p3dobj(const NPVariant *variant); private: bool read_contents_file(const string &filename); @@ -65,7 +65,6 @@ private: void send_window(); void show_np_variant(const NPVariant &result); - P3D_object *np_variant_to_object(const NPVariant &result); #ifdef _WIN32 static LONG diff --git a/direct/src/plugin_npapi/ppPandaObject.cxx b/direct/src/plugin_npapi/ppPandaObject.cxx index 70cb6fe786..5ba542b03d 100644 --- a/direct/src/plugin_npapi/ppPandaObject.cxx +++ b/direct/src/plugin_npapi/ppPandaObject.cxx @@ -175,7 +175,7 @@ get_property(NPIdentifier name, NPVariant *result) { } // We have the property, and its value is stored in value. - _instance->object_to_variant(result, value); + _instance->p3dobj_to_variant(result, value); P3D_OBJECT_FINISH(value); return true; } @@ -195,7 +195,7 @@ set_property(NPIdentifier name, const NPVariant *value) { return false; } - P3D_object *object = _instance->variant_to_object(value); + P3D_object *object = _instance->variant_to_p3dobj(value); bool result = P3D_OBJECT_SET_PROPERTY(_p3d_object, property_name.c_str(), object); return result; } diff --git a/direct/src/showutil/runp3d.py b/direct/src/showutil/runp3d.py index 7ec7745b65..9ac750021e 100755 --- a/direct/src/showutil/runp3d.py +++ b/direct/src/showutil/runp3d.py @@ -22,7 +22,7 @@ See pack3d.py for a script that generates these p3d files. import sys from direct.showbase import VFSImporter from direct.showbase.DirectObject import DirectObject -from pandac.PandaModules import VirtualFileSystem, Filename, Multifile, loadPrcFileData, unloadPrcFile, getModelPath, HTTPClient +from pandac.PandaModules import VirtualFileSystem, Filename, Multifile, loadPrcFileData, unloadPrcFile, getModelPath, HTTPClient, Thread from direct.stdpy import file from direct.task.TaskManagerGlobal import taskMgr from direct.showbase import AppRunnerGlobal @@ -155,8 +155,9 @@ class AppRunner(DirectObject): """ Replaces self.window with the browser's toplevel DOM object, for controlling the JavaScript and the document in the same page with the Panda3D plugin. """ - print "setBrowserScriptObject(%s)" % (window) + self.window = window + print "setBrowserScriptObject(%s)" % (window) def setP3DFilename(self, p3dFilename, tokens = [], instanceId = None): @@ -261,7 +262,8 @@ class AppRunner(DirectObject): self.requestFunc = func def sendRequest(self, request, *args): - self.requestFunc(self.instanceId, request, args) + assert self.requestFunc + return self.requestFunc(self.instanceId, request, args) def windowEvent(self, win): print "Got window event in runp3d" @@ -271,7 +273,9 @@ class AppRunner(DirectObject): def scriptRequest(self, operation, object, propertyName = None, value = None): """ Issues a new script request to the browser. This queries - or modifies one of the browser's DOM properties. + or modifies one of the browser's DOM properties. This method + blocks until the return value is received from the browser, + and then it returns that value. operation may be one of [ 'get_property', 'set_property', 'call', 'evaluate' ]. @@ -291,10 +295,10 @@ class AppRunner(DirectObject): self.sendRequest('script', operation, object, propertyName, value, uniqueId); - def scriptResponse(self, uniqueId, value): - """ Called by the browser in response to a scriptRequest, - above. """ - print "Got scriptResponse: %s, %s" % (uniqueId, value) + # Now wait for the response to come in. + result = self.sendRequest('wait_script_response', uniqueId) + print "result for %s.%s = %s" % (object, propertyName, result,) + return result def parseSysArgs(self): """ Converts sys.argv into (p3dFilename, tokens). """ @@ -333,9 +337,47 @@ class BrowserObject: actually exists in the plugin host's namespace, e.g. a JavaScript or DOM object. """ - def __init__(self, objectId): - self.__objectId = objectId - + def __init__(self, runner, objectId): + self.__dict__['_BrowserObject__runner'] = runner + self.__dict__['_BrowserObject__objectId'] = objectId + + def __str__(self): + return "BrowserObject(%s)" % (self.__objectId) + + def __nonzero__(self): + return True + + def __getattr__(self, name): + """ Remaps attempts to query an attribute into the appropriate + calls to query the actual browser object under the hood. """ + + print "__getattr_(self, %s)" % (name) + print "runner = %s" % (self.__runner) + value = self.__runner.scriptRequest('get_property', self, + propertyName = name) + return value + # raise AttributeError(name) + + def __setattr__(self, name, value): + if name in self.__dict__: + self.__dict__[name] = value + return + + value = self.__runner.scriptRequest('set_property', self, + propertyName = name, + value = value) + if not value: + raise AttributeError(name) + + def __delattr__(self, name): + if name in self.__dict__: + del self.__dict__[name] + return + + value = self.__runner.scriptRequest('del_property', self, + propertyName = name) + if not value: + raise AttributeError(name) if __name__ == '__main__': runner = AppRunner()