diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index 5a98e79bb1..46a8c898c0 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/direct/src/plugin/p3dInstance.cxx @@ -266,7 +266,9 @@ get_request() { case P3D_RT_script: handle_script_request(result); - break; + // Completely eat this request; don't return it to the caller. + finish_request(result, true); + return get_request(); default: // Other kinds of requests don't require special handling at @@ -520,8 +522,14 @@ handle_script_request(P3D_request *request) { case P3D_SO_set_property: { - bool result = P3D_OBJECT_SET_PROPERTY(object, request->_request._script._property_name, - request->_request._script._value); + bool result; + if (request->_request._script._num_values == 1) { + result = P3D_OBJECT_SET_PROPERTY(object, request->_request._script._property_name, + request->_request._script._values[0]); + } else { + // Wrong number of values. Error. + result = false; + } // Feed the result back down to the subprocess. TiXmlDocument *doc = new TiXmlDocument; @@ -565,6 +573,36 @@ handle_script_request(P3D_request *request) { _session->send_command(doc); } break; + + case P3D_SO_call: + { + P3D_object *result = + P3D_OBJECT_CALL(object, request->_request._script._property_name, + request->_request._script._values, + request->_request._script._num_values); + // Reset the value count to 0 so we won't double-delete the + // values when the request is deleted (the above call has + // already deleted them). + request->_request._script._num_values = 0; + + // Feed the result back down to the subprocess. + TiXmlDocument *doc = new TiXmlDocument; + TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", ""); + TiXmlElement *xcommand = new TiXmlElement("command"); + xcommand->SetAttribute("cmd", "script_response"); + xcommand->SetAttribute("unique_id", unique_id); + + doc->LinkEndChild(decl); + doc->LinkEndChild(xcommand); + + if (result != NULL) { + xcommand->LinkEndChild(_session->p3dobj_to_xml(result)); + P3D_OBJECT_FINISH(result); + } + + _session->send_command(doc); + } + break; } } diff --git a/direct/src/plugin/p3dPythonRun.cxx b/direct/src/plugin/p3dPythonRun.cxx index 47c3e1ca1d..d631f4f725 100755 --- a/direct/src/plugin/p3dPythonRun.cxx +++ b/direct/src/plugin/p3dPythonRun.cxx @@ -474,6 +474,7 @@ task_check_comm(GenericAsyncTask *task, void *user_data) { //////////////////////////////////////////////////////////////////// TiXmlDocument *P3DPythonRun:: wait_script_response(int response_id) { + nout << "Waiting script_response " << response_id << "\n"; while (true) { ACQUIRE_LOCK(_commands_lock); @@ -491,7 +492,7 @@ wait_script_response(int response_id) { // This is the response we were waiting for. _commands.erase(ci); RELEASE_LOCK(_commands_lock); - nout << "received: " << *doc << "\n" << flush; + nout << "received script_response: " << *doc << "\n" << flush; return doc; } } @@ -525,6 +526,7 @@ wait_script_response(int response_id) { // process the input and append it to the queue. Thread::force_yield(); } + assert(false); } //////////////////////////////////////////////////////////////////// @@ -549,11 +551,11 @@ py_request_func(PyObject *args) { // on the wire. int response_id; if (!PyArg_ParseTuple(extra_args, "i", &response_id)) { - Py_DECREF(extra_args); return NULL; } TiXmlDocument *doc = wait_script_response(response_id); + assert(doc != NULL); TiXmlElement *xcommand = doc->FirstChildElement("command"); assert(xcommand != NULL); TiXmlElement *xvalue = xcommand->FirstChildElement("value"); @@ -568,7 +570,6 @@ py_request_func(PyObject *args) { } delete doc; - Py_DECREF(extra_args); return value; } @@ -584,7 +585,6 @@ 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; } @@ -601,7 +601,6 @@ 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; } @@ -611,8 +610,26 @@ py_request_func(PyObject *args) { TiXmlElement *xobject = pyobj_to_xml(object); xobject->SetValue("object"); xrequest->LinkEndChild(xobject); - TiXmlElement *xvalue = pyobj_to_xml(value); - xrequest->LinkEndChild(xvalue); + + if (strcmp(operation, "call") == 0 && PySequence_Check(value)) { + // A special case: operation "call" receives a tuple of + // parameters; unpack the tuple for the XML. + Py_ssize_t length = PySequence_Length(value); + for (Py_ssize_t i = 0; i < length; ++i) { + PyObject *p = PySequence_GetItem(value, i); + if (p != NULL) { + TiXmlElement *xvalue = pyobj_to_xml(p); + xrequest->LinkEndChild(xvalue); + Py_DECREF(p); + } + } + + } else { + // Other kinds of operations receive only a single parameter, if + // any. + TiXmlElement *xvalue = pyobj_to_xml(value); + xrequest->LinkEndChild(xvalue); + } nout << "sent: " << doc << "\n" << flush; _pipe_write << doc << flush; @@ -620,11 +637,9 @@ 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(""); } diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx index c246c08e61..f5305c62f8 100644 --- a/direct/src/plugin/p3dSession.cxx +++ b/direct/src/plugin/p3dSession.cxx @@ -684,41 +684,54 @@ rt_make_p3d_request(TiXmlElement *xrequest) { const char *operation = xrequest->Attribute("operation"); TiXmlElement *xobject = xrequest->FirstChildElement("object"); const char *property_name = xrequest->Attribute("property_name"); - TiXmlElement *xvalue = xrequest->FirstChildElement("value"); int unique_id = 0; xrequest->Attribute("unique_id", &unique_id); if (operation != NULL && xobject != NULL) { - P3D_object *object = xml_to_p3dobj(xobject); - if (strcmp(operation, "get_property") == 0 && property_name != NULL) { - request = new P3D_request; - request->_request_type = P3D_RT_script; - request->_request._script._object = object; - request->_request._script._op = P3D_SO_get_property; - request->_request._script._property_name = strdup(property_name); - request->_request._script._value = NULL; - request->_request._script._unique_id = unique_id; - } else if (strcmp(operation, "set_property") == 0 && property_name != NULL && xvalue != NULL) { - request = new P3D_request; - request->_request_type = P3D_RT_script; - request->_request._script._object = object; - request->_request._script._op = P3D_SO_set_property; - request->_request._script._property_name = strdup(property_name); - request->_request._script._value = xml_to_p3dobj(xvalue); - request->_request._script._unique_id = unique_id; - } else if (strcmp(operation, "del_property") == 0 && property_name != NULL) { - request = new P3D_request; - request->_request_type = P3D_RT_script; - request->_request._script._object = object; - request->_request._script._op = P3D_SO_del_property; - request->_request._script._property_name = strdup(property_name); - request->_request._script._value = NULL; - request->_request._script._unique_id = unique_id; + P3D_script_operation op; + if (strcmp(operation, "get_property") == 0) { + op = P3D_SO_get_property; + } else if (strcmp(operation, "set_property") == 0) { + op = P3D_SO_set_property; + } else if (strcmp(operation, "del_property") == 0) { + op = P3D_SO_del_property; + } else if (strcmp(operation, "call") == 0) { + op = P3D_SO_call; + } else { + // An unexpected operation. + return NULL; } - if (request == NULL) { - // If we haven't dispatched a request yet, we didn't use the - // object, so delete it. - P3D_OBJECT_FINISH(object); + + request = new P3D_request; + request->_request_type = P3D_RT_script; + request->_request._script._op = op; + request->_request._script._object = xml_to_p3dobj(xobject); + request->_request._script._property_name = NULL; + if (property_name != NULL) { + request->_request._script._property_name = strdup(property_name); + } + request->_request._script._unique_id = unique_id; + + // Fill in the value(s). + vector values; + TiXmlElement *xvalue = xrequest->FirstChildElement("value"); + while (xvalue != NULL) { + P3D_object *value = xml_to_p3dobj(xvalue); + values.push_back(value); + xvalue = xvalue->NextSiblingElement("value"); + } + + if (values.empty()) { + // No values. + request->_request._script._values = NULL; + request->_request._script._num_values = 0; + } else { + // Some values. + int num_values = (int)values.size(); + P3D_object **valuesp = new P3D_object *[num_values]; + memcpy(valuesp, &values[0], num_values * sizeof(P3D_object *)); + request->_request._script._values = valuesp; + request->_request._script._num_values = num_values; } } diff --git a/direct/src/plugin/p3d_plugin.h b/direct/src/plugin/p3d_plugin.h index c52c87e935..aeb9fa255c 100644 --- a/direct/src/plugin/p3d_plugin.h +++ b/direct/src/plugin/p3d_plugin.h @@ -609,7 +609,8 @@ typedef struct { P3D_object *_object; P3D_script_operation _op; const char *_property_name; - P3D_object *_value; + P3D_object **_values; + int _num_values; int _unique_id; } P3D_request_script; diff --git a/direct/src/plugin_npapi/ppBrowserObject.cxx b/direct/src/plugin_npapi/ppBrowserObject.cxx index 896ba2a552..e67df57331 100644 --- a/direct/src/plugin_npapi/ppBrowserObject.cxx +++ b/direct/src/plugin_npapi/ppBrowserObject.cxx @@ -184,10 +184,39 @@ set_property(const string &property, P3D_object *value) { //////////////////////////////////////////////////////////////////// P3D_object *PPBrowserObject:: call(const string &method_name, P3D_object *params[], int num_params) const { + // First, convert all of the parameters. + NPVariant *npparams = new NPVariant[num_params]; for (int i = 0; i < num_params; ++i) { - P3D_OBJECT_FINISH(params[i]); + _instance->p3dobj_to_variant(&npparams[i], params[i]); } - return NULL; + + NPVariant result; + if (method_name.empty()) { + // Call the default method. + if (!browser->invokeDefault(_instance->get_npp_instance(), _npobj, + npparams, num_params, &result)) { + // Failed to invoke. + logfile << "invoke failed\n" << flush; + delete[] npparams; + return NULL; + } + } else { + // Call the named method. + + NPIdentifier method_id = browser->getstringidentifier(method_name.c_str()); + if (!browser->invoke(_instance->get_npp_instance(), _npobj, method_id, + npparams, num_params, &result)) { + // Failed to invoke. + delete[] npparams; + return NULL; + } + } + + logfile << "invoke succeeded\n" << flush; + + P3D_object *object = _instance->variant_to_p3dobj(&result); + browser->releasevariantvalue(&result); + return object; } //////////////////////////////////////////////////////////////////// diff --git a/direct/src/plugin_npapi/startup.cxx b/direct/src/plugin_npapi/startup.cxx index ef8fea971b..bcc13dc5f8 100644 --- a/direct/src/plugin_npapi/startup.cxx +++ b/direct/src/plugin_npapi/startup.cxx @@ -313,7 +313,7 @@ NPP_Print(NPP instance, NPPrint *platformPrint) { //////////////////////////////////////////////////////////////////// int16 NPP_HandleEvent(NPP instance, void *event) { - logfile << "HandleEvent\n" << flush; + // logfile << "HandleEvent\n" << flush; // Here's a fine opportunity to check for new requests. PPInstance::handle_request_loop(); diff --git a/direct/src/plugin_standalone/panda3d.cxx b/direct/src/plugin_standalone/panda3d.cxx index 3398cc5c34..66b5fc1375 100644 --- a/direct/src/plugin_standalone/panda3d.cxx +++ b/direct/src/plugin_standalone/panda3d.cxx @@ -405,33 +405,6 @@ handle_request(P3D_request *request) { cerr << "Got P3D_RT_notify: " << request->_request._notify._message << "\n"; // Ignore notifications. - - /* - { - // Temporary. - P3D_object *obj = P3D_instance_get_panda_script_object(request->_instance); - cerr << "script_object is " << obj << "\n"; - if (obj != NULL) { - if (P3D_OBJECT_SET_PROPERTY(obj, "foo", P3D_new_int_object(1))) { - cerr << "set_property succeeded\n"; - P3D_object *prop = P3D_OBJECT_GET_PROPERTY(obj, "foo"); - cerr << "get_property returned " << prop << "\n"; - if (prop != NULL) { - int size = P3D_OBJECT_GET_STRING(prop, NULL, 0) + 1; - char *buffer = new char[size]; - P3D_OBJECT_GET_STRING(prop, buffer, size); - cerr << " property is " << buffer << "\n"; - delete buffer; - P3D_OBJECT_FINISH(prop); - } - } else { - cerr << "set_property failed\n"; - } - P3D_OBJECT_FINISH(obj); - } - } - */ - break; default: diff --git a/direct/src/showutil/runp3d.py b/direct/src/showutil/runp3d.py index dc98ee3550..b416fd2a06 100755 --- a/direct/src/showutil/runp3d.py +++ b/direct/src/showutil/runp3d.py @@ -284,7 +284,7 @@ class AppRunner(DirectObject): def windowEvent(self, win): self.sendRequest('notify', 'onwindowopen') - def scriptRequest(self, operation, object, propertyName = None, + def scriptRequest(self, operation, object, propertyName = '', value = None): """ Issues a new script request to the browser. This queries or modifies one of the browser's DOM properties. This method @@ -364,12 +364,37 @@ class BrowserObject: self.__dict__['_BrowserObject__runner'] = runner self.__dict__['_BrowserObject__objectId'] = objectId + # This element is filled in by __getattr__; it connects + # the object to its parent. + self.__dict__['_BrowserObject__boundMethod'] = (None, None) + def __str__(self): - return "BrowserObject(%s)" % (self.__objectId) + parentObj, attribName = self.__boundMethod + if parentObj: + # Format it from its parent. + return "%s.%s" % (parentObj, attribName) + else: + # Format it directly. + return "BrowserObject(%s)" % (self.__objectId) def __nonzero__(self): return True + def __call__(self, *args): + parentObj, attribName = self.__boundMethod + if parentObj: + # Call it as a method. + result = self.__runner.scriptRequest('call', parentObj, propertyName = attribName, value = args) + else: + # Call it as a plain function. + result = self.__runner.scriptRequest('call', self, value = args) + + if result is Null: + # Could not call the method. + raise TypeError + + return result + def __getattr__(self, name): """ Remaps attempts to query an attribute, as in obj.attr, into the appropriate calls to query the actual browser object @@ -381,6 +406,13 @@ class BrowserObject: # Failed to retrieve the attribute. raise AttributeError(name) + if isinstance(value, BrowserObject): + # Fill in the parent object association, so __call__ can + # properly call a method. (Javascript needs to know the + # method container at the time of the call, and doesn't + # store it on the function object.) + value.__dict__['_BrowserObject__boundMethod'] = (self, name) + return value def __setattr__(self, name, value):