diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index a4e84eff2e..54d751a411 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/direct/src/plugin/p3dInstance.cxx @@ -37,8 +37,6 @@ typedef P3DSplashWindow SplashWindowType; #endif #endif -int P3DInstance::_next_instance_id = 0; - //////////////////////////////////////////////////////////////////// // Function: P3DInstance::Constructor // Access: Public @@ -55,8 +53,8 @@ P3DInstance(P3D_request_ready_func *func, void *user_data) : _got_fparams = false; _got_wparams = false; - _instance_id = _next_instance_id; - ++_next_instance_id; + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + _instance_id = inst_mgr->get_unique_id(); INIT_LOCK(_request_lock); @@ -123,7 +121,7 @@ set_fparams(const P3DFileParams &fparams) { // For the moment, all sessions will be unique. P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); ostringstream strm; - strm << inst_mgr->get_unique_session_index(); + strm << inst_mgr->get_unique_id(); _session_key = strm.str(); // TODO. @@ -457,8 +455,8 @@ void P3DInstance:: start_download(P3DDownload *download) { assert(download->get_download_id() == 0); - int download_id = _next_instance_id; - ++_next_instance_id; + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + int download_id = inst_mgr->get_unique_id(); download->set_download_id(download_id); bool inserted = _downloads.insert(Downloads::value_type(download_id, download)).second; diff --git a/direct/src/plugin/p3dInstance.h b/direct/src/plugin/p3dInstance.h index 230aa568ab..912e8e0a49 100644 --- a/direct/src/plugin/p3dInstance.h +++ b/direct/src/plugin/p3dInstance.h @@ -139,8 +139,6 @@ private: typedef deque BakedRequests; BakedRequests _baked_requests; - static int _next_instance_id; - friend class P3DSession; friend class SplashDownload; }; diff --git a/direct/src/plugin/p3dInstanceManager.cxx b/direct/src/plugin/p3dInstanceManager.cxx index e998427d29..c4579ed094 100644 --- a/direct/src/plugin/p3dInstanceManager.cxx +++ b/direct/src/plugin/p3dInstanceManager.cxx @@ -40,7 +40,7 @@ P3DInstanceManager *P3DInstanceManager::_global_ptr; P3DInstanceManager:: P3DInstanceManager() { _is_initialized = false; - _unique_session_index = 0; + _unique_id = 0; _notify_thread_continue = false; _started_notify_thread = false; @@ -280,16 +280,17 @@ get_package(const string &package_name, const string &package_version, } //////////////////////////////////////////////////////////////////// -// Function: P3DInstanceManager::get_unique_session_index +// Function: P3DInstanceManager::get_unique_id // Access: Public -// Description: Returns a number used to uniquify the session_key for -// different instances. This number is guaranteed to be -// different at each call. +// Description: Returns a number used to uniquify different +// instances. This number is guaranteed to be different +// at each call, at least until the int space rolls +// over. //////////////////////////////////////////////////////////////////// int P3DInstanceManager:: -get_unique_session_index() { - ++_unique_session_index; - return _unique_session_index; +get_unique_id() { + ++_unique_id; + return _unique_id; } //////////////////////////////////////////////////////////////////// diff --git a/direct/src/plugin/p3dInstanceManager.h b/direct/src/plugin/p3dInstanceManager.h index 28c4c88921..5b8aa48ce1 100644 --- a/direct/src/plugin/p3dInstanceManager.h +++ b/direct/src/plugin/p3dInstanceManager.h @@ -61,7 +61,7 @@ public: inline int get_num_instances() const; - int get_unique_session_index(); + int get_unique_id(); void signal_request_ready(P3DInstance *inst); P3D_class_definition *make_class_definition() const; @@ -100,7 +100,7 @@ private: typedef map Packages; Packages _packages; - int _unique_session_index; + int _unique_id; // This condition var is waited on the main thread and signaled in a // sub-thread when new request notices arrive. diff --git a/direct/src/plugin/p3dPythonRun.cxx b/direct/src/plugin/p3dPythonRun.cxx index b851bfe77e..828dff5d81 100755 --- a/direct/src/plugin/p3dPythonRun.cxx +++ b/direct/src/plugin/p3dPythonRun.cxx @@ -32,6 +32,7 @@ P3DPythonRun(int argc, char *argv[]) { INIT_LOCK(_commands_lock); INIT_THREAD(_read_thread); + _session_id = 0; _next_sent_id = 0; _program_name = argv[0]; @@ -223,7 +224,20 @@ handle_command(TiXmlDocument *doc) { const char *cmd = xcommand->Attribute("cmd"); if (cmd != NULL) { - if (strcmp(cmd, "start_instance") == 0) { + if (strcmp(cmd, "init") == 0) { + assert(!needs_response); + + // The only purpose of the "init" command is to send us a + // unique session ID, which in fact we don't do much with. + xcommand->Attribute("session_id", &_session_id); + + // We do use it to initiate our object id sequence with a + // number at least a little bit distinct from other sessions, + // though. No technical requirement that we do this, but it + // does make debugging the logs a bit easier. + _next_sent_id = _session_id * 1000; + + } else if (strcmp(cmd, "start_instance") == 0) { assert(!needs_response); TiXmlElement *xinstance = xcommand->FirstChildElement("instance"); if (xinstance != (TiXmlElement *)NULL) { @@ -405,6 +419,7 @@ handle_pyobj_command(TiXmlElement *xcommand, bool needs_response, if (PyArg_ParseTuple(params, "s", &property_name)) { if (PyObject_HasAttrString(obj, property_name)) { result = PyObject_GetAttrString(obj, property_name); + } else { result = NULL; } diff --git a/direct/src/plugin/p3dPythonRun.h b/direct/src/plugin/p3dPythonRun.h index 1bee044a1d..963e65976d 100755 --- a/direct/src/plugin/p3dPythonRun.h +++ b/direct/src/plugin/p3dPythonRun.h @@ -105,6 +105,8 @@ private: typedef pmap Instances; Instances _instances; + int _session_id; + string _program_name; int _py_argc; char **_py_argv; diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx index 42ed8e7885..6d3a32041b 100644 --- a/direct/src/plugin/p3dSession.cxx +++ b/direct/src/plugin/p3dSession.cxx @@ -38,13 +38,12 @@ //////////////////////////////////////////////////////////////////// P3DSession:: P3DSession(P3DInstance *inst) { + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + _session_id = inst_mgr->get_unique_id(); _session_key = inst->get_session_key(); _python_version = inst->get_python_version(); - _next_sent_id = 0; - _p3dpython_running = false; - _next_response_id = 0; _response = NULL; _got_response_id = -1; @@ -58,8 +57,6 @@ P3DSession(P3DInstance *inst) { INIT_LOCK(_instances_lock); INIT_THREAD(_read_thread); - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - _panda3d = inst_mgr->get_package("panda3d", "dev", "Panda3D"); _python_root_dir = _panda3d->get_package_dir(); inst->add_package(_panda3d); @@ -261,8 +258,8 @@ command_and_response(TiXmlDocument *command) { return NULL; } - int response_id = _next_response_id; - ++_next_response_id; + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + int response_id = inst_mgr->get_unique_id(); // Add the "want_response_id" attribute to the toplevel command, so // the sub-process knows we'll be waiting for its response. @@ -474,15 +471,14 @@ p3dobj_to_xml(P3D_object *obj) { // should pass a reference down to this particular object, so // the Python process knows to call back up to here to query it. - int object_id = _next_sent_id; - ++_next_sent_id; + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + int object_id = inst_mgr->get_unique_id(); bool inserted = _sent_objects.insert(SentObjects::value_type(object_id, obj)).second; while (!inserted) { // Hmm, we must have cycled around the entire int space? Either // that, or there's a logic bug somewhere. Assume the former, // and keep looking for an empty slot. - object_id = _next_sent_id; - ++_next_sent_id; + object_id = inst_mgr->get_unique_id(); inserted = _sent_objects.insert(SentObjects::value_type(object_id, obj)).second; } @@ -656,16 +652,28 @@ start_p3dpython() { spawn_read_thread(); - // Now that the process has been started, feed it any commands we - // may have queued up. + // The very first command we send to the process is its session_id. + TiXmlDocument doc; + TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", ""); + TiXmlElement *xcommand = new TiXmlElement("command"); + xcommand->SetAttribute("cmd", "init"); + xcommand->SetAttribute("session_id", _session_id); + doc.LinkEndChild(decl); + doc.LinkEndChild(xcommand); + nout << "sent: " << doc << "\n" << flush; + _pipe_write << doc; + + // Also feed it any commands we may have queued up from before the + // process was started. Commands::iterator ci; for (ci = _commands.begin(); ci != _commands.end(); ++ci) { nout << "sent: " << *(*ci) << "\n" << flush; _pipe_write << *(*ci); delete (*ci); } - _pipe_write << flush; _commands.clear(); + + _pipe_write << flush; } //////////////////////////////////////////////////////////////////// diff --git a/direct/src/plugin/p3dSession.h b/direct/src/plugin/p3dSession.h index 45d096af62..aa0c030667 100644 --- a/direct/src/plugin/p3dSession.h +++ b/direct/src/plugin/p3dSession.h @@ -98,6 +98,7 @@ private: }; private: + int _session_id; string _session_key; string _python_version; string _output_filename; @@ -119,7 +120,6 @@ private: // child process tells us it's safe to delete them. typedef map SentObjects; SentObjects _sent_objects; - int _next_sent_id; P3DPackage *_panda3d; PackageCallback *_panda3d_callback; @@ -132,8 +132,6 @@ private: #endif bool _p3dpython_running; - int _next_response_id; - // The _response_ready mutex protects this pointer. TiXmlDocument *_response; int _got_response_id; diff --git a/direct/src/plugin_npapi/ppPandaObject.cxx b/direct/src/plugin_npapi/ppPandaObject.cxx index 7c3d06d9b2..d18c879972 100644 --- a/direct/src/plugin_npapi/ppPandaObject.cxx +++ b/direct/src/plugin_npapi/ppPandaObject.cxx @@ -221,7 +221,13 @@ has_property(NPIdentifier name) { // will never be called. So we always say we *do* have any // particular property, whether we currently have it right now or // not (since we *could* have it if you call set_property()). - return true; + + // On the other hand, Firefox gets confused about methods that are + // also properties. So you have to say there's *no* property if + // there is in fact a callable method by that name, or Firefox will + // never call the method. + bool result = P3D_OBJECT_HAS_METHOD(_p3d_object, property_name.c_str()); + return !result; } //////////////////////////////////////////////////////////////////// diff --git a/direct/src/showutil/runp3d.py b/direct/src/showutil/runp3d.py index 1c24ae1a96..c3419c8bd4 100755 --- a/direct/src/showutil/runp3d.py +++ b/direct/src/showutil/runp3d.py @@ -518,7 +518,12 @@ class BrowserObject: value = self.__runner.scriptRequest('get_property', self, propertyName = name) except EnvironmentError: - # Failed to retrieve the attribute. + # Failed to retrieve the attribute. But maybe there's a + # method instead? + if self.__runner.scriptRequest('has_method', self, propertyName = name): + # Yes, so create a method wrapper for it. + return MethodWrapper(self.__runner, self, name) + raise AttributeError(name) if isinstance(value, BrowserObject): @@ -591,6 +596,64 @@ class BrowserObject: else: raise IndexError(key) +class MethodWrapper: + """ This is a Python wrapper around a property of a BrowserObject + that doesn't appear to be a first-class object in the Python + sense, but is nonetheless a callable method. """ + + def __init__(self, runner, parentObj, objectId): + self.__dict__['_MethodWrapper__runner'] = runner + self.__dict__['_MethodWraper__boundMethod'] = (parentObj, objectId) + + def __str__(self): + parentObj, attribName = self.__boundMethod + return "%s.%s" % (parentObj, attribName) + + def __nonzero__(self): + return True + + def __call__(self, *args): + try: + parentObj, attribName = self.__boundMethod + # Call it as a method. + needsResponse = True + if parentObj is self.__runner.dom and attribName == 'alert': + # As a special hack, we don't wait for the return + # value from the alert() call, since this is a + # blocking call, and waiting for this could cause + # problems. + needsResponse = False + + if parentObj is self.__runner.dom and attribName == 'eval' and len(args) == 1 and isinstance(args[0], types.StringTypes): + # As another special hack, we make dom.eval() a + # special case, and map it directly into an eval() + # call. If the string begins with 'void ', we further + # assume we're not waiting for a response. + if args[0].startswith('void '): + needsResponse = False + result = self.__runner.scriptRequest('eval', parentObj, value = args[0], needsResponse = needsResponse) + else: + # This is a normal method call. + try: + result = self.__runner.scriptRequest('call', parentObj, propertyName = attribName, value = args, needsResponse = needsResponse) + except EnvironmentError: + # Problem on the call. Maybe no such method? + raise AttributeError + + except EnvironmentError: + # Some odd problem on the call. + raise TypeError + + return result + + def __setattr__(self, name, value): + """ setattr will generally fail on method objects. """ + raise AttributeError(name) + + def __delattr__(self, name): + """ delattr will generally fail on method objects. """ + raise AttributeError(name) + if __name__ == '__main__': runner = AppRunner() runner.gotWindow = True